ferns-ui 1.13.0 → 1.15.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.
- package/dist/ActionSheet.js +14 -11
- package/dist/ActionSheet.js.map +1 -1
- package/dist/AddressField.js +1 -1
- package/dist/AddressField.js.map +1 -1
- package/dist/Badge.js +1 -1
- package/dist/Badge.js.map +1 -1
- package/dist/Banner.js +1 -1
- package/dist/Banner.js.map +1 -1
- package/dist/Box.js +3 -3
- package/dist/Box.js.map +1 -1
- package/dist/Box.test.js +36 -36
- package/dist/Box.test.js.map +1 -1
- package/dist/CheckBox.js.map +1 -1
- package/dist/Common.d.ts +6 -1
- package/dist/DataTable.js +2 -3
- package/dist/DataTable.js.map +1 -1
- package/dist/DateTimeField.js +22 -22
- package/dist/DateTimeField.js.map +1 -1
- package/dist/FernsProvider.js +1 -1
- package/dist/FernsProvider.js.map +1 -1
- package/dist/Heading.js +1 -1
- package/dist/Heading.js.map +1 -1
- package/dist/Hyperlink.js +1 -1
- package/dist/Hyperlink.js.map +1 -1
- package/dist/IconButton.js +1 -1
- package/dist/IconButton.js.map +1 -1
- package/dist/Image.js.map +1 -1
- package/dist/MobileAddressAutoComplete.js +1 -1
- package/dist/MobileAddressAutoComplete.js.map +1 -1
- package/dist/Modal.d.ts +1 -1
- package/dist/Modal.js +29 -16
- package/dist/Modal.js.map +1 -1
- package/dist/NumberField.js +10 -4
- package/dist/NumberField.js.map +1 -1
- package/dist/NumberPickerActionSheet.d.ts +1 -3
- package/dist/NumberPickerActionSheet.js +0 -3
- package/dist/NumberPickerActionSheet.js.map +1 -1
- package/dist/Page.js +1 -1
- package/dist/Page.js.map +1 -1
- package/dist/Pagination.js +2 -2
- package/dist/Pagination.js.map +1 -1
- package/dist/Permissions.d.ts +1 -1
- package/dist/Permissions.js +2 -2
- package/dist/Permissions.js.map +1 -1
- package/dist/PickerSelect.js +1 -1
- package/dist/PickerSelect.js.map +1 -1
- package/dist/SectionDivider.js +1 -1
- package/dist/SectionDivider.js.map +1 -1
- package/dist/SegmentedControl.js.map +1 -1
- package/dist/Signature.native.js +2 -2
- package/dist/Signature.native.js.map +1 -1
- package/dist/SignatureField.js +2 -2
- package/dist/SignatureField.js.map +1 -1
- package/dist/Slider.js +3 -3
- package/dist/Slider.js.map +1 -1
- package/dist/SplitPage.js +7 -7
- package/dist/SplitPage.js.map +1 -1
- package/dist/SplitPage.native.js +4 -6
- package/dist/SplitPage.native.js.map +1 -1
- package/dist/TapToEdit.js +3 -3
- package/dist/TapToEdit.js.map +1 -1
- package/dist/Text.js +1 -1
- package/dist/Text.js.map +1 -1
- package/dist/TextArea.test.js.map +1 -1
- package/dist/TextField.test.js +3 -3
- package/dist/TextField.test.js.map +1 -1
- package/dist/TextFieldNumberActionSheet.d.ts +2 -4
- package/dist/TextFieldNumberActionSheet.js +1 -4
- package/dist/TextFieldNumberActionSheet.js.map +1 -1
- package/dist/Tooltip.js +37 -19
- package/dist/Tooltip.js.map +1 -1
- package/dist/Unifier.d.ts +0 -1
- package/dist/Unifier.js.map +1 -1
- package/dist/Utilities.d.ts +1 -1
- package/dist/Utilities.js +2 -3
- package/dist/Utilities.js.map +1 -1
- package/dist/WebAddressAutocomplete.js +2 -1
- package/dist/WebAddressAutocomplete.js.map +1 -1
- package/dist/index.d.ts +10 -11
- package/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/dist/table/Table.js +14 -15
- package/dist/table/Table.js.map +1 -1
- package/dist/table/TableHeaderCell.js +2 -2
- package/dist/table/TableHeaderCell.js.map +1 -1
- package/dist/useStoredState.js +4 -2
- package/dist/useStoredState.js.map +1 -1
- package/package.json +5 -19
- package/src/ActionSheet.tsx +26 -22
- package/src/AddressField.tsx +1 -1
- package/src/Badge.tsx +1 -1
- package/src/Banner.tsx +1 -1
- package/src/Box.test.tsx +71 -70
- package/src/Box.tsx +21 -9
- package/src/CheckBox.tsx +7 -1
- package/src/Common.ts +6 -1
- package/src/DataTable.tsx +2 -5
- package/src/DateTimeField.tsx +22 -22
- package/src/FernsProvider.tsx +1 -4
- package/src/Heading.tsx +1 -1
- package/src/Hyperlink.tsx +1 -1
- package/src/IconButton.tsx +2 -2
- package/src/Image.tsx +1 -0
- package/src/MobileAddressAutoComplete.tsx +1 -1
- package/src/Modal.tsx +41 -20
- package/src/NumberField.tsx +10 -4
- package/src/NumberPickerActionSheet.tsx +1 -5
- package/src/Page.tsx +1 -1
- package/src/Pagination.tsx +2 -11
- package/src/Permissions.ts +2 -2
- package/src/PickerSelect.tsx +1 -1
- package/src/SectionDivider.tsx +1 -1
- package/src/SegmentedControl.tsx +3 -1
- package/src/Signature.native.tsx +2 -2
- package/src/SignatureField.tsx +2 -2
- package/src/Slider.tsx +10 -17
- package/src/SplitPage.native.tsx +2 -4
- package/src/SplitPage.tsx +4 -4
- package/src/TapToEdit.tsx +3 -7
- package/src/Text.tsx +1 -1
- package/src/TextArea.test.tsx +27 -43
- package/src/TextField.test.tsx +3 -4
- package/src/TextFieldNumberActionSheet.tsx +3 -7
- package/src/Tooltip.tsx +44 -22
- package/src/Unifier.ts +1 -3
- package/src/Utilities.tsx +3 -4
- package/src/WebAddressAutocomplete.tsx +1 -1
- package/src/index.tsx +11 -11
- package/src/table/Table.tsx +34 -36
- package/src/table/TableHeaderCell.tsx +2 -2
- package/src/useStoredState.ts +13 -11
package/src/Text.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useFonts,
|
|
9
9
|
} from "@expo-google-fonts/nunito";
|
|
10
10
|
import React from "react";
|
|
11
|
-
import {
|
|
11
|
+
import {Text as NativeText, Platform, TextStyle} from "react-native";
|
|
12
12
|
|
|
13
13
|
import {TextProps} from "./Common";
|
|
14
14
|
import {Hyperlink} from "./Hyperlink";
|
package/src/TextArea.test.tsx
CHANGED
|
@@ -20,7 +20,7 @@ describe("TextArea", () => {
|
|
|
20
20
|
const {getByDisplayValue} = renderWithTheme(
|
|
21
21
|
<TextArea value="test content" onChange={mockOnChange} />
|
|
22
22
|
);
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
const input = getByDisplayValue("test content");
|
|
25
25
|
expect(input.props.multiline).toBe(true);
|
|
26
26
|
expect(input.props.value).toBe("test content");
|
|
@@ -30,7 +30,7 @@ describe("TextArea", () => {
|
|
|
30
30
|
const {getByText} = renderWithTheme(
|
|
31
31
|
<TextArea title="Description" value="" onChange={mockOnChange} />
|
|
32
32
|
);
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
expect(getByText("Description")).toBeTruthy();
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -38,7 +38,7 @@ describe("TextArea", () => {
|
|
|
38
38
|
const {getByPlaceholderText} = renderWithTheme(
|
|
39
39
|
<TextArea placeholder="Enter description" value="" onChange={mockOnChange} />
|
|
40
40
|
);
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
expect(getByPlaceholderText("Enter description")).toBeTruthy();
|
|
43
43
|
});
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ describe("TextArea", () => {
|
|
|
46
46
|
const {getByText} = renderWithTheme(
|
|
47
47
|
<TextArea helperText="Maximum 500 characters" value="" onChange={mockOnChange} />
|
|
48
48
|
);
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
expect(getByText("Maximum 500 characters")).toBeTruthy();
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -54,7 +54,7 @@ describe("TextArea", () => {
|
|
|
54
54
|
const {getByText} = renderWithTheme(
|
|
55
55
|
<TextArea errorText="This field is required" value="" onChange={mockOnChange} />
|
|
56
56
|
);
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
expect(getByText("This field is required")).toBeTruthy();
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -62,7 +62,7 @@ describe("TextArea", () => {
|
|
|
62
62
|
const {getByDisplayValue} = renderWithTheme(
|
|
63
63
|
<TextArea grow value="" onChange={mockOnChange} />
|
|
64
64
|
);
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
const input = getByDisplayValue("");
|
|
67
67
|
expect(input.props.multiline).toBe(true);
|
|
68
68
|
});
|
|
@@ -71,16 +71,14 @@ describe("TextArea", () => {
|
|
|
71
71
|
const {getByDisplayValue} = renderWithTheme(
|
|
72
72
|
<TextArea disabled value="test" onChange={mockOnChange} />
|
|
73
73
|
);
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
const input = getByDisplayValue("test");
|
|
76
76
|
expect(input.props.readOnly).toBe(true);
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
it("should always have text type", () => {
|
|
80
|
-
const {getByDisplayValue} = renderWithTheme(
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
|
|
80
|
+
const {getByDisplayValue} = renderWithTheme(<TextArea value="" onChange={mockOnChange} />);
|
|
81
|
+
|
|
84
82
|
const input = getByDisplayValue("");
|
|
85
83
|
expect(input.props.keyboardType).toBe("default");
|
|
86
84
|
});
|
|
@@ -89,9 +87,7 @@ describe("TextArea", () => {
|
|
|
89
87
|
describe("user interactions", () => {
|
|
90
88
|
it("should call onChange when text is entered", async () => {
|
|
91
89
|
const user = userEvent.setup();
|
|
92
|
-
const {getByDisplayValue} = renderWithTheme(
|
|
93
|
-
<TextArea value="" onChange={mockOnChange} />
|
|
94
|
-
);
|
|
90
|
+
const {getByDisplayValue} = renderWithTheme(<TextArea value="" onChange={mockOnChange} />);
|
|
95
91
|
|
|
96
92
|
const input = getByDisplayValue("");
|
|
97
93
|
await user.type(input, "hello world");
|
|
@@ -103,9 +99,7 @@ describe("TextArea", () => {
|
|
|
103
99
|
it("should handle multiline text input", async () => {
|
|
104
100
|
const user = userEvent.setup();
|
|
105
101
|
const multilineText = "Line 1\nLine 2\nLine 3";
|
|
106
|
-
const {getByDisplayValue} = renderWithTheme(
|
|
107
|
-
<TextArea value="" onChange={mockOnChange} />
|
|
108
|
-
);
|
|
102
|
+
const {getByDisplayValue} = renderWithTheme(<TextArea value="" onChange={mockOnChange} />);
|
|
109
103
|
|
|
110
104
|
const input = getByDisplayValue("");
|
|
111
105
|
await user.type(input, multilineText);
|
|
@@ -117,10 +111,8 @@ describe("TextArea", () => {
|
|
|
117
111
|
|
|
118
112
|
describe("accessibility", () => {
|
|
119
113
|
it("should have correct accessibility properties", () => {
|
|
120
|
-
const {getByDisplayValue} = renderWithTheme(
|
|
121
|
-
|
|
122
|
-
);
|
|
123
|
-
|
|
114
|
+
const {getByDisplayValue} = renderWithTheme(<TextArea value="" onChange={mockOnChange} />);
|
|
115
|
+
|
|
124
116
|
const input = getByDisplayValue("");
|
|
125
117
|
expect(input.props.accessibilityHint).toBe("Enter text here");
|
|
126
118
|
expect(input.props["aria-label"]).toBe("Text input field");
|
|
@@ -130,7 +122,7 @@ describe("TextArea", () => {
|
|
|
130
122
|
const {getByDisplayValue} = renderWithTheme(
|
|
131
123
|
<TextArea disabled value="" onChange={mockOnChange} />
|
|
132
124
|
);
|
|
133
|
-
|
|
125
|
+
|
|
134
126
|
const input = getByDisplayValue("");
|
|
135
127
|
expect(input.props.accessibilityState.disabled).toBe(true);
|
|
136
128
|
});
|
|
@@ -138,19 +130,15 @@ describe("TextArea", () => {
|
|
|
138
130
|
|
|
139
131
|
describe("edge cases", () => {
|
|
140
132
|
it("should handle empty value", () => {
|
|
141
|
-
const {getByDisplayValue} = renderWithTheme(
|
|
142
|
-
|
|
143
|
-
);
|
|
144
|
-
|
|
133
|
+
const {getByDisplayValue} = renderWithTheme(<TextArea value="" onChange={mockOnChange} />);
|
|
134
|
+
|
|
145
135
|
const input = getByDisplayValue("");
|
|
146
136
|
expect(input.props.value).toBe("");
|
|
147
137
|
});
|
|
148
138
|
|
|
149
139
|
it("should handle undefined value", () => {
|
|
150
|
-
const {root} = renderWithTheme(
|
|
151
|
-
|
|
152
|
-
);
|
|
153
|
-
|
|
140
|
+
const {root} = renderWithTheme(<TextArea value={undefined} onChange={mockOnChange} />);
|
|
141
|
+
|
|
154
142
|
expect(root).toBeTruthy();
|
|
155
143
|
});
|
|
156
144
|
|
|
@@ -159,7 +147,7 @@ describe("TextArea", () => {
|
|
|
159
147
|
const {getByDisplayValue} = renderWithTheme(
|
|
160
148
|
<TextArea value={longText} onChange={mockOnChange} />
|
|
161
149
|
);
|
|
162
|
-
|
|
150
|
+
|
|
163
151
|
const input = getByDisplayValue(longText);
|
|
164
152
|
expect(input.props.value).toBe(longText);
|
|
165
153
|
});
|
|
@@ -169,7 +157,7 @@ describe("TextArea", () => {
|
|
|
169
157
|
const {getByDisplayValue} = renderWithTheme(
|
|
170
158
|
<TextArea value={textWithBreaks} onChange={mockOnChange} />
|
|
171
159
|
);
|
|
172
|
-
|
|
160
|
+
|
|
173
161
|
const input = getByDisplayValue(textWithBreaks);
|
|
174
162
|
expect(input.props.value).toBe(textWithBreaks);
|
|
175
163
|
});
|
|
@@ -179,10 +167,10 @@ describe("TextArea", () => {
|
|
|
179
167
|
it("should inherit all TextField props except multiline and type", () => {
|
|
180
168
|
const mockOnFocus = jest.fn();
|
|
181
169
|
const mockOnBlur = jest.fn();
|
|
182
|
-
|
|
170
|
+
|
|
183
171
|
const {getByDisplayValue} = renderWithTheme(
|
|
184
|
-
<TextArea
|
|
185
|
-
value="test"
|
|
172
|
+
<TextArea
|
|
173
|
+
value="test"
|
|
186
174
|
onChange={mockOnChange}
|
|
187
175
|
onFocus={mockOnFocus}
|
|
188
176
|
onBlur={mockOnBlur}
|
|
@@ -191,7 +179,7 @@ describe("TextArea", () => {
|
|
|
191
179
|
rows={5}
|
|
192
180
|
/>
|
|
193
181
|
);
|
|
194
|
-
|
|
182
|
+
|
|
195
183
|
const input = getByDisplayValue("test");
|
|
196
184
|
expect(input.props.numberOfLines).toBe(5);
|
|
197
185
|
expect(input.props.onFocus).toBeTruthy();
|
|
@@ -200,19 +188,15 @@ describe("TextArea", () => {
|
|
|
200
188
|
|
|
201
189
|
it("should support inputRef", () => {
|
|
202
190
|
const mockInputRef = jest.fn();
|
|
203
|
-
renderWithTheme(
|
|
204
|
-
|
|
205
|
-
);
|
|
206
|
-
|
|
191
|
+
renderWithTheme(<TextArea inputRef={mockInputRef} value="" onChange={mockOnChange} />);
|
|
192
|
+
|
|
207
193
|
expect(mockInputRef).toHaveBeenCalled();
|
|
208
194
|
});
|
|
209
195
|
});
|
|
210
196
|
|
|
211
197
|
describe("snapshots", () => {
|
|
212
198
|
it("should match snapshot with default props", () => {
|
|
213
|
-
const component = renderWithTheme(
|
|
214
|
-
<TextArea value="test content" onChange={mockOnChange} />
|
|
215
|
-
);
|
|
199
|
+
const component = renderWithTheme(<TextArea value="test content" onChange={mockOnChange} />);
|
|
216
200
|
expect(component.toJSON()).toMatchSnapshot();
|
|
217
201
|
});
|
|
218
202
|
|
package/src/TextField.test.tsx
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {act, userEvent} from "@testing-library/react-native";
|
|
2
2
|
import React from "react";
|
|
3
|
-
|
|
4
|
-
import {renderWithTheme} from "./test-utils";
|
|
5
3
|
import {TextField} from "./TextField";
|
|
4
|
+
import {renderWithTheme} from "./test-utils";
|
|
6
5
|
|
|
7
6
|
describe("TextField", () => {
|
|
8
7
|
let mockOnChange: jest.Mock;
|
|
@@ -103,7 +102,7 @@ describe("TextField", () => {
|
|
|
103
102
|
});
|
|
104
103
|
|
|
105
104
|
it("should call onEnter when enter key is pressed", async () => {
|
|
106
|
-
const
|
|
105
|
+
const _user = userEvent.setup();
|
|
107
106
|
const {getByDisplayValue} = renderWithTheme(
|
|
108
107
|
<TextField value="" onChange={mockOnChange} onEnter={mockOnEnter} />
|
|
109
108
|
);
|
|
@@ -206,7 +205,7 @@ describe("TextField", () => {
|
|
|
206
205
|
});
|
|
207
206
|
|
|
208
207
|
it("should not call onFocus when disabled", async () => {
|
|
209
|
-
const
|
|
208
|
+
const _user = userEvent.setup();
|
|
210
209
|
const {getByDisplayValue} = renderWithTheme(
|
|
211
210
|
<TextField disabled value="" onChange={mockOnChange} onFocus={mockOnFocus} />
|
|
212
211
|
);
|
|
@@ -4,18 +4,14 @@ import React from "react";
|
|
|
4
4
|
import {ActionSheet} from "./ActionSheet";
|
|
5
5
|
import {Box} from "./Box";
|
|
6
6
|
import {Button} from "./Button";
|
|
7
|
-
import {
|
|
7
|
+
import {TextFieldPickerActionSheetProps} from "./Common";
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
type NumberPickerActionSheetState = {};
|
|
10
10
|
|
|
11
11
|
export class NumberPickerActionSheet extends React.Component<
|
|
12
12
|
TextFieldPickerActionSheetProps,
|
|
13
13
|
NumberPickerActionSheetState
|
|
14
14
|
> {
|
|
15
|
-
constructor(props: NumberPickerActionSheetProps) {
|
|
16
|
-
super(props);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
15
|
render() {
|
|
20
16
|
return (
|
|
21
17
|
<ActionSheet ref={this.props.actionSheetRef} bounceOnOpen gestureEnabled>
|
|
@@ -36,7 +32,7 @@ export class NumberPickerActionSheet extends React.Component<
|
|
|
36
32
|
mode={this.props.mode}
|
|
37
33
|
testID="dateTimePicker"
|
|
38
34
|
value={this.props.value ? new Date(this.props.value) : new Date()}
|
|
39
|
-
onChange={(
|
|
35
|
+
onChange={(_event: any, date?: Date) => {
|
|
40
36
|
if (!date) {
|
|
41
37
|
return;
|
|
42
38
|
}
|
package/src/Tooltip.tsx
CHANGED
|
@@ -19,20 +19,20 @@ const TOOLTIP_OFFSET = 6;
|
|
|
19
19
|
// How many pixels to leave between the tooltip and the edge of the screen
|
|
20
20
|
const TOOLTIP_OVERFLOW_PADDING = 20;
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
interface ChildrenMeasurement {
|
|
23
23
|
width: number;
|
|
24
24
|
height: number;
|
|
25
25
|
pageX: number;
|
|
26
26
|
pageY: number;
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
28
|
|
|
29
29
|
// empty object is a fallback for when the tooltip is not measured yet
|
|
30
|
-
|
|
30
|
+
interface Measurement {
|
|
31
31
|
children: ChildrenMeasurement | {};
|
|
32
32
|
tooltip: LayoutRectangle | {};
|
|
33
33
|
measured: boolean;
|
|
34
34
|
idealPosition?: TooltipPosition;
|
|
35
|
-
}
|
|
35
|
+
}
|
|
36
36
|
|
|
37
37
|
const getTooltipPosition = ({
|
|
38
38
|
children,
|
|
@@ -182,20 +182,33 @@ export const Tooltip: FC<TooltipProps> = ({text, children, idealPosition, includ
|
|
|
182
182
|
const childrenWrapperRef = useRef<View>(null);
|
|
183
183
|
const touched = useRef(false);
|
|
184
184
|
const isWeb = Platform.OS === "web";
|
|
185
|
+
const resetMeasurement = useCallback(() => {
|
|
186
|
+
setMeasurement({
|
|
187
|
+
children: {},
|
|
188
|
+
tooltip: {},
|
|
189
|
+
measured: false,
|
|
190
|
+
});
|
|
191
|
+
}, []);
|
|
192
|
+
const hideTooltip = useCallback(() => {
|
|
193
|
+
if (showTooltipTimer.current) {
|
|
194
|
+
clearTimeout(showTooltipTimer.current);
|
|
195
|
+
}
|
|
196
|
+
if (hideTooltipTimer.current) {
|
|
197
|
+
clearTimeout(hideTooltipTimer.current);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
touched.current = false;
|
|
201
|
+
setVisible(false);
|
|
202
|
+
resetMeasurement();
|
|
203
|
+
}, [resetMeasurement, setVisible]);
|
|
185
204
|
|
|
186
205
|
// If the tooltip is visible, and the user clicks outside of the tooltip, hide it.
|
|
187
206
|
useEffect(() => {
|
|
188
207
|
return () => {
|
|
189
|
-
if (showTooltipTimer.current) {
|
|
190
|
-
clearTimeout(showTooltipTimer.current);
|
|
191
|
-
}
|
|
192
|
-
if (hideTooltipTimer.current) {
|
|
193
|
-
clearTimeout(hideTooltipTimer.current);
|
|
194
|
-
}
|
|
195
208
|
// Hide tooltip on unmount to prevent it from staying stuck on screen
|
|
196
|
-
|
|
209
|
+
hideTooltip();
|
|
197
210
|
};
|
|
198
|
-
}, []);
|
|
211
|
+
}, [hideTooltip]);
|
|
199
212
|
|
|
200
213
|
const getArrowContainerStyle = (): ViewStyle => {
|
|
201
214
|
if (!includeArrow) {
|
|
@@ -258,6 +271,11 @@ export const Tooltip: FC<TooltipProps> = ({text, children, idealPosition, includ
|
|
|
258
271
|
);
|
|
259
272
|
|
|
260
273
|
const handleTouchStart = useCallback(() => {
|
|
274
|
+
if (visible) {
|
|
275
|
+
hideTooltip();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
261
279
|
if (hideTooltipTimer.current) {
|
|
262
280
|
clearTimeout(hideTooltipTimer.current);
|
|
263
281
|
}
|
|
@@ -266,7 +284,7 @@ export const Tooltip: FC<TooltipProps> = ({text, children, idealPosition, includ
|
|
|
266
284
|
touched.current = true;
|
|
267
285
|
setVisible(true);
|
|
268
286
|
}, 100);
|
|
269
|
-
}, []);
|
|
287
|
+
}, [hideTooltip, visible]);
|
|
270
288
|
|
|
271
289
|
const handleHoverIn = useCallback(() => {
|
|
272
290
|
if (hideTooltipTimer.current) {
|
|
@@ -280,20 +298,23 @@ export const Tooltip: FC<TooltipProps> = ({text, children, idealPosition, includ
|
|
|
280
298
|
}, [hoverDelay]);
|
|
281
299
|
|
|
282
300
|
const handleHoverOut = useCallback(() => {
|
|
283
|
-
touched.current = false;
|
|
284
301
|
if (showTooltipTimer.current) {
|
|
285
302
|
clearTimeout(showTooltipTimer.current);
|
|
286
303
|
}
|
|
304
|
+
if (hideTooltipTimer.current) {
|
|
305
|
+
clearTimeout(hideTooltipTimer.current);
|
|
306
|
+
}
|
|
287
307
|
|
|
288
308
|
hideTooltipTimer.current = setTimeout(() => {
|
|
289
|
-
|
|
290
|
-
setMeasurement({
|
|
291
|
-
children: {},
|
|
292
|
-
tooltip: {},
|
|
293
|
-
measured: false,
|
|
294
|
-
});
|
|
309
|
+
hideTooltip();
|
|
295
310
|
}, hoverEndDelay);
|
|
296
|
-
}, [hoverEndDelay]);
|
|
311
|
+
}, [hideTooltip, hoverEndDelay]);
|
|
312
|
+
|
|
313
|
+
const handleClick = useCallback(() => {
|
|
314
|
+
if (visible) {
|
|
315
|
+
hideTooltip();
|
|
316
|
+
}
|
|
317
|
+
}, [hideTooltip, visible]);
|
|
297
318
|
|
|
298
319
|
const mobilePressProps = {
|
|
299
320
|
onPress: useCallback(() => {
|
|
@@ -348,7 +369,7 @@ export const Tooltip: FC<TooltipProps> = ({text, children, idealPosition, includ
|
|
|
348
369
|
borderRadius: theme.radius.default,
|
|
349
370
|
}}
|
|
350
371
|
testID="tooltip-container"
|
|
351
|
-
onPress={
|
|
372
|
+
onPress={hideTooltip}
|
|
352
373
|
>
|
|
353
374
|
<Text color="inverted" size="sm">
|
|
354
375
|
{text}
|
|
@@ -369,6 +390,7 @@ export const Tooltip: FC<TooltipProps> = ({text, children, idealPosition, includ
|
|
|
369
390
|
handleHoverOut();
|
|
370
391
|
children.props.onHoverOut?.();
|
|
371
392
|
}}
|
|
393
|
+
onPress={isWeb ? handleClick : undefined}
|
|
372
394
|
onTouchStart={handleTouchStart}
|
|
373
395
|
{...(!isWeb && mobilePressProps)}
|
|
374
396
|
>
|
package/src/Unifier.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as Clipboard from "expo-clipboard";
|
|
|
6
6
|
import * as Haptics from "expo-haptics";
|
|
7
7
|
import {Dimensions, Keyboard, Linking, Platform, Vibration} from "react-native";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {PermissionKind} from "./Common";
|
|
10
10
|
import {requestPermissions} from "./Permissions";
|
|
11
11
|
|
|
12
12
|
declare global {
|
|
@@ -61,8 +61,6 @@ export function changeColorLuminance(hex: string, luminanceChange: Luminance) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
class UnifierClass {
|
|
64
|
-
private _theme?: Partial<FernsTheme>;
|
|
65
|
-
|
|
66
64
|
private _web = false;
|
|
67
65
|
|
|
68
66
|
private _dev = false;
|
package/src/Utilities.tsx
CHANGED
|
@@ -18,8 +18,7 @@ export function mergeInlineStyles(inlineStyle?: any, newStyle?: any) {
|
|
|
18
18
|
|
|
19
19
|
export function isTestUser(profile?: BaseProfile) {
|
|
20
20
|
return (
|
|
21
|
-
profile &&
|
|
22
|
-
profile.email &&
|
|
21
|
+
profile?.email &&
|
|
23
22
|
(profile.email.indexOf("nang.io") > -1 || profile.email.indexOf("example.com") > -1)
|
|
24
23
|
);
|
|
25
24
|
}
|
|
@@ -57,7 +56,7 @@ order in which you do so doesn't really matter.
|
|
|
57
56
|
*/
|
|
58
57
|
|
|
59
58
|
interface InlineStyle {
|
|
60
|
-
[key: string]: string | number |
|
|
59
|
+
[key: string]: string | number | undefined;
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
// TODO: This type should be opaque, however the Babel parser doesn't support
|
|
@@ -318,7 +317,7 @@ export function formattedCountyCode(state: string, countyName: string): string {
|
|
|
318
317
|
}
|
|
319
318
|
|
|
320
319
|
export function isAPIError(error: any): error is APIError {
|
|
321
|
-
return error
|
|
320
|
+
return error?.data?.title;
|
|
322
321
|
}
|
|
323
322
|
|
|
324
323
|
export function printAPIError(error: APIError, details = true): string {
|
|
@@ -7,7 +7,7 @@ import {processAddressComponents} from "./Utilities";
|
|
|
7
7
|
|
|
8
8
|
const loadGooglePlacesScript = (googleMapsApiKey: string, callbackName: any): Promise<void> => {
|
|
9
9
|
return new Promise<void>((resolve, reject): undefined => {
|
|
10
|
-
if (window.google
|
|
10
|
+
if (window.google?.maps?.places) {
|
|
11
11
|
resolve();
|
|
12
12
|
return;
|
|
13
13
|
}
|
package/src/index.tsx
CHANGED
|
@@ -34,6 +34,7 @@ export * from "./ImageBackground";
|
|
|
34
34
|
export * from "./InfoModalIcon";
|
|
35
35
|
export * from "./InfoTooltipButton";
|
|
36
36
|
export * from "./Link";
|
|
37
|
+
export * from "./MarkdownView";
|
|
37
38
|
export * from "./MediaQuery";
|
|
38
39
|
export * from "./MobileAddressAutoComplete";
|
|
39
40
|
export * from "./Modal";
|
|
@@ -59,11 +60,17 @@ export * from "./SignatureField";
|
|
|
59
60
|
export * from "./Slider";
|
|
60
61
|
export * from "./Spinner";
|
|
61
62
|
export * from "./SplitPage";
|
|
63
|
+
export * from "./TapToEdit";
|
|
64
|
+
export * from "./Text";
|
|
65
|
+
export * from "./TextArea";
|
|
66
|
+
export * from "./TextField";
|
|
67
|
+
export * from "./Theme";
|
|
68
|
+
export * from "./Toast";
|
|
69
|
+
export * from "./Tooltip";
|
|
62
70
|
export * from "./table/Table";
|
|
63
71
|
export * from "./table/Table";
|
|
64
72
|
export * from "./table/TableBadge";
|
|
65
73
|
export * from "./table/TableBoolean";
|
|
66
|
-
export * from "./table/tableContext";
|
|
67
74
|
export * from "./table/TableDate";
|
|
68
75
|
export * from "./table/TableHeader";
|
|
69
76
|
export * from "./table/TableHeaderCell";
|
|
@@ -72,19 +79,13 @@ export * from "./table/TableNumber";
|
|
|
72
79
|
export * from "./table/TableRow";
|
|
73
80
|
export * from "./table/TableText";
|
|
74
81
|
export * from "./table/TableTitle";
|
|
75
|
-
export * from "./
|
|
76
|
-
export * from "./Text";
|
|
77
|
-
export * from "./TextArea";
|
|
78
|
-
export * from "./TextField";
|
|
79
|
-
export * from "./Theme";
|
|
80
|
-
export * from "./Toast";
|
|
81
|
-
export * from "./Tooltip";
|
|
82
|
-
export * from "./MarkdownView";
|
|
82
|
+
export * from "./table/tableContext";
|
|
83
83
|
export * from "./UnifiedAddressAutoComplete";
|
|
84
84
|
export * from "./Unifier";
|
|
85
|
-
export * from "./useStoredState";
|
|
86
85
|
export * from "./Utilities";
|
|
86
|
+
export * from "./useStoredState";
|
|
87
87
|
export * from "./WebAddressAutocomplete";
|
|
88
|
+
|
|
88
89
|
// export * from "./Layout";
|
|
89
90
|
// export * from "./Drawer";
|
|
90
91
|
// export * from "./Chart";
|
|
@@ -1371,4 +1372,3 @@ export interface Options {
|
|
|
1371
1372
|
*/
|
|
1372
1373
|
blurOnUnmount?: boolean;
|
|
1373
1374
|
}
|
|
1374
|
-
export {};
|
package/src/table/Table.tsx
CHANGED
|
@@ -68,44 +68,42 @@ export const Table = ({
|
|
|
68
68
|
sortColumn={sortColumn}
|
|
69
69
|
stickyHeader={stickyHeader}
|
|
70
70
|
>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
(child
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)}
|
|
95
|
-
</ScrollView>
|
|
71
|
+
<Box
|
|
72
|
+
flex="grow"
|
|
73
|
+
maxWidth="100%"
|
|
74
|
+
style={{
|
|
75
|
+
position: "relative",
|
|
76
|
+
}}
|
|
77
|
+
width={width}
|
|
78
|
+
>
|
|
79
|
+
<ScrollView horizontal style={{width, maxWidth: "100%"}}>
|
|
80
|
+
{/* TODO: Replace table scrollview with flat list */}
|
|
81
|
+
<ScrollView
|
|
82
|
+
// nestedScrollEnabled
|
|
83
|
+
stickyHeaderIndices={stickyHeader ? [0] : undefined}
|
|
84
|
+
style={{width, maxWidth: "100%", flex: 1, maxHeight}}
|
|
85
|
+
>
|
|
86
|
+
{Children.map(
|
|
87
|
+
children,
|
|
88
|
+
(child, index) =>
|
|
89
|
+
Boolean(child) &&
|
|
90
|
+
React.cloneElement(child as ReactElement, {
|
|
91
|
+
color: index % 2 === 1 && alternateRowBackground ? "neutralLight" : "base",
|
|
92
|
+
})
|
|
93
|
+
)}
|
|
96
94
|
</ScrollView>
|
|
95
|
+
</ScrollView>
|
|
96
|
+
</Box>
|
|
97
|
+
{Boolean(shouldPaginate && totalPages !== undefined) && (
|
|
98
|
+
<Box alignItems="center" borderTop="default" direction="row" height={60} paddingX={8}>
|
|
99
|
+
<Pagination
|
|
100
|
+
page={propsPage ?? page}
|
|
101
|
+
setPage={propsSetPage ?? setPage}
|
|
102
|
+
totalPages={totalPages!}
|
|
103
|
+
/>
|
|
104
|
+
{Boolean(extraControls) && extraControls}
|
|
97
105
|
</Box>
|
|
98
|
-
|
|
99
|
-
<Box alignItems="center" borderTop="default" direction="row" height={60} paddingX={8}>
|
|
100
|
-
<Pagination
|
|
101
|
-
page={propsPage ?? page}
|
|
102
|
-
setPage={propsSetPage ?? setPage}
|
|
103
|
-
totalPages={totalPages!}
|
|
104
|
-
/>
|
|
105
|
-
{Boolean(extraControls) && extraControls}
|
|
106
|
-
</Box>
|
|
107
|
-
)}
|
|
108
|
-
</>
|
|
106
|
+
)}
|
|
109
107
|
</TableContextProvider>
|
|
110
108
|
);
|
|
111
109
|
};
|
|
@@ -6,8 +6,8 @@ import {View} from "react-native";
|
|
|
6
6
|
import {Box} from "../Box";
|
|
7
7
|
import {AlignItems, TableHeaderCellProps} from "../Common";
|
|
8
8
|
import {useTheme} from "../Theme";
|
|
9
|
-
import {useTableContext} from "./tableContext";
|
|
10
9
|
import {TableTitle} from "./TableTitle";
|
|
10
|
+
import {useTableContext} from "./tableContext";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Use TableHeaderCell to define a header cell in Table.
|
|
@@ -48,7 +48,7 @@ export const TableHeaderCell = ({
|
|
|
48
48
|
if (setSortColumn) {
|
|
49
49
|
setSortColumn(newSort ? {column: index, direction: newSort} : undefined);
|
|
50
50
|
}
|
|
51
|
-
onSortChange
|
|
51
|
+
onSortChange?.(newSort);
|
|
52
52
|
}, [index, onSortChange, setSortColumn, sort]);
|
|
53
53
|
|
|
54
54
|
if (sortable && !onSortChange) {
|
package/src/useStoredState.ts
CHANGED
|
@@ -22,17 +22,19 @@ export const useStoredState = <T>(
|
|
|
22
22
|
|
|
23
23
|
// Fetch data when the component mounts
|
|
24
24
|
useEffect(() => {
|
|
25
|
-
void fetchData()
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
void fetchData()
|
|
26
|
+
.then((value) => {
|
|
27
|
+
if (isMounted.current) {
|
|
28
|
+
setState(value);
|
|
29
|
+
setIsLoading(false);
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
.catch((error) => {
|
|
33
|
+
console.error("Error fetching data:", error);
|
|
34
|
+
if (isMounted.current) {
|
|
35
|
+
setIsLoading(false);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
36
38
|
|
|
37
39
|
return () => {
|
|
38
40
|
isMounted.current = false;
|