ferns-ui 0.4.3 → 0.6.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/Box.js +3 -3
- package/dist/Box.js.map +1 -1
- package/dist/Common.d.ts +1 -16
- package/dist/Common.js.map +1 -1
- package/dist/Field.d.ts +16 -1
- package/dist/Field.js +30 -35
- package/dist/Field.js.map +1 -1
- package/dist/Icon.js +0 -1
- package/dist/Icon.js.map +1 -1
- package/dist/PickerSelect.js +3 -5
- package/dist/PickerSelect.js.map +1 -1
- package/dist/Pog.js +1 -97
- package/dist/Pog.js.map +1 -1
- package/dist/SelectList.d.ts +3 -8
- package/dist/SelectList.js +17 -23
- package/dist/SelectList.js.map +1 -1
- package/dist/SplitPage.js.map +1 -1
- package/dist/TapToEdit.d.ts +11 -11
- package/dist/TapToEdit.js +72 -22
- package/dist/TapToEdit.js.map +1 -1
- package/dist/Unifier.js +0 -1
- package/dist/Unifier.js.map +1 -1
- package/package.json +25 -24
- package/src/Box.tsx +3 -3
- package/src/Button.tsx +1 -1
- package/src/Common.ts +0 -30
- package/src/Field.tsx +71 -40
- package/src/Icon.tsx +1 -3
- package/src/Permissions.ts +1 -1
- package/src/PickerSelect.tsx +3 -5
- package/src/Pog.tsx +0 -124
- package/src/SelectList.tsx +26 -30
- package/src/SplitPage.tsx +0 -2
- package/src/TapToEdit.tsx +101 -31
- package/src/Unifier.ts +0 -2
package/src/Pog.tsx
CHANGED
|
@@ -5,125 +5,6 @@ import {Box} from "./Box";
|
|
|
5
5
|
import {AllColors, IconPrefix, IconSize} from "./Common";
|
|
6
6
|
import {Icon} from "./Icon";
|
|
7
7
|
|
|
8
|
-
/*
|
|
9
|
-
Originally based on https://github.com/pinterest/gestalt
|
|
10
|
-
Forked, added type definitions, and added features.
|
|
11
|
-
*/
|
|
12
|
-
// TOOD: create styles
|
|
13
|
-
// :root {
|
|
14
|
-
// --lightGray: #efefef;
|
|
15
|
-
// --gray: #8e8e8e;
|
|
16
|
-
// --darkGray: #111;
|
|
17
|
-
// }
|
|
18
|
-
|
|
19
|
-
// .pog {
|
|
20
|
-
// composes: circle from "./Borders.module.css";
|
|
21
|
-
// composes: flex from "./Layout.module.css";
|
|
22
|
-
// composes: itemsCenter from "./Layout.module.css";
|
|
23
|
-
// composes: justifyCenter from "./Layout.module.css";
|
|
24
|
-
// }
|
|
25
|
-
|
|
26
|
-
// .focused {
|
|
27
|
-
// composes: accessibilityOutlineFocus from "./Focus.module.css";
|
|
28
|
-
// }
|
|
29
|
-
|
|
30
|
-
// .selected {
|
|
31
|
-
// composes: darkGrayBg from "./Colors.module.css";
|
|
32
|
-
// }
|
|
33
|
-
|
|
34
|
-
// .transparent {
|
|
35
|
-
// composes: transparentBg from "./Colors.module.css";
|
|
36
|
-
// }
|
|
37
|
-
|
|
38
|
-
// .transparentDarkGray {
|
|
39
|
-
// composes: transparentDarkGrayBg from "./Colors.module.css";
|
|
40
|
-
// }
|
|
41
|
-
|
|
42
|
-
// .transparent.hovered,
|
|
43
|
-
// .transparent.focused,
|
|
44
|
-
// .white.hovered,
|
|
45
|
-
// .white.focused {
|
|
46
|
-
// background-color: rgba(0, 0, 0, 0.06);
|
|
47
|
-
// }
|
|
48
|
-
|
|
49
|
-
// .transparent.active,
|
|
50
|
-
// .white.active {
|
|
51
|
-
// background-color: rgba(0, 0, 0, 0.1);
|
|
52
|
-
// }
|
|
53
|
-
|
|
54
|
-
// .transparentDarkGray.hovered,
|
|
55
|
-
// .transparentDarkGray.focused {
|
|
56
|
-
// background-color: var(--darkGray);
|
|
57
|
-
// }
|
|
58
|
-
|
|
59
|
-
// .transparentDarkGray.active {
|
|
60
|
-
// background-color: var(--darkGray);
|
|
61
|
-
// }
|
|
62
|
-
|
|
63
|
-
// .white {
|
|
64
|
-
// composes: whiteBg from "./Colors.module.css";
|
|
65
|
-
// }
|
|
66
|
-
|
|
67
|
-
// .white.hovered,
|
|
68
|
-
// .white.focused {
|
|
69
|
-
// background-color: #f0f0f0;
|
|
70
|
-
// }
|
|
71
|
-
|
|
72
|
-
// .white.active {
|
|
73
|
-
// background-color: #e5e5e5;
|
|
74
|
-
// }
|
|
75
|
-
|
|
76
|
-
// .lightGray {
|
|
77
|
-
// composes: lightGrayBg from "./Colors.module.css";
|
|
78
|
-
// }
|
|
79
|
-
|
|
80
|
-
// .lightGray.hovered,
|
|
81
|
-
// .lightGray.focused {
|
|
82
|
-
// background-color: #e2e2e2;
|
|
83
|
-
// }
|
|
84
|
-
|
|
85
|
-
// .lightGray.active {
|
|
86
|
-
// background-color: #dadada;
|
|
87
|
-
// }
|
|
88
|
-
|
|
89
|
-
// .gray {
|
|
90
|
-
// composes: grayBg from "./Colors.module.css";
|
|
91
|
-
// }
|
|
92
|
-
|
|
93
|
-
// .gray.hovered,
|
|
94
|
-
// .gray.focused {
|
|
95
|
-
// background-color: #878787;
|
|
96
|
-
// }
|
|
97
|
-
|
|
98
|
-
// .gray.active {
|
|
99
|
-
// background-color: #828282;
|
|
100
|
-
// }
|
|
101
|
-
|
|
102
|
-
// .darkGray {
|
|
103
|
-
// composes: darkGrayBg from "./Colors.module.css";
|
|
104
|
-
// }
|
|
105
|
-
|
|
106
|
-
// .blue {
|
|
107
|
-
// composes: blueBg from "./Colors.module.css";
|
|
108
|
-
// }
|
|
109
|
-
|
|
110
|
-
// .blue.hovered,
|
|
111
|
-
// .blue.focused {
|
|
112
|
-
// background-color: #4a8ad4;
|
|
113
|
-
// }
|
|
114
|
-
|
|
115
|
-
// .blue.active {
|
|
116
|
-
// background-color: #4a85c9;
|
|
117
|
-
// }
|
|
118
|
-
|
|
119
|
-
const styles = {
|
|
120
|
-
pog: "",
|
|
121
|
-
selected: "",
|
|
122
|
-
active: "",
|
|
123
|
-
focused: "",
|
|
124
|
-
hovered: "",
|
|
125
|
-
};
|
|
126
|
-
|
|
127
8
|
const SIZE_NAME_TO_PIXEL = {
|
|
128
9
|
xs: 24,
|
|
129
10
|
sm: 32,
|
|
@@ -132,7 +13,6 @@ const SIZE_NAME_TO_PIXEL = {
|
|
|
132
13
|
xl: 56,
|
|
133
14
|
};
|
|
134
15
|
|
|
135
|
-
// type PogColor = "gray" | "darkGray" | "red" | "blue" | "white" | "orange";
|
|
136
16
|
interface Props {
|
|
137
17
|
active?: boolean;
|
|
138
18
|
bgColor?: "transparent" | "transparentDarkGray" | "gray" | "lightGray" | "white" | "blue";
|
|
@@ -157,10 +37,7 @@ const defaultIconButtonIconColors = {
|
|
|
157
37
|
|
|
158
38
|
export default function Pog(props: Props) {
|
|
159
39
|
const {
|
|
160
|
-
active = false,
|
|
161
40
|
bgColor = "transparent",
|
|
162
|
-
focused = false,
|
|
163
|
-
hovered = false,
|
|
164
41
|
iconColor,
|
|
165
42
|
icon,
|
|
166
43
|
iconPrefix = "fas",
|
|
@@ -168,7 +45,6 @@ export default function Pog(props: Props) {
|
|
|
168
45
|
size = "md",
|
|
169
46
|
} = props;
|
|
170
47
|
|
|
171
|
-
const iconSize = SIZE_NAME_TO_PIXEL[size] / 2;
|
|
172
48
|
const color =
|
|
173
49
|
(selected && "white") ||
|
|
174
50
|
iconColor ||
|
package/src/SelectList.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import RNPickerSelect from "./PickerSelect";
|
|
|
5
5
|
import {Unifier} from "./Unifier";
|
|
6
6
|
|
|
7
7
|
// Use "" if you want to have an "unset" value.
|
|
8
|
-
export type SelectListOptions = {label: string; value: string
|
|
8
|
+
export type SelectListOptions = {label: string; value: string}[];
|
|
9
9
|
export interface SelectListProps extends FieldWithLabelsProps {
|
|
10
10
|
id?: string;
|
|
11
11
|
name?: string;
|
|
@@ -17,33 +17,29 @@ export interface SelectListProps extends FieldWithLabelsProps {
|
|
|
17
17
|
placeholder?: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
onValueChange={this.props.onChange}
|
|
46
|
-
/>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
20
|
+
export function SelectList({options, value, onChange}: SelectListProps) {
|
|
21
|
+
return (
|
|
22
|
+
<RNPickerSelect
|
|
23
|
+
items={options}
|
|
24
|
+
placeholder={{}}
|
|
25
|
+
style={{
|
|
26
|
+
viewContainer: {
|
|
27
|
+
flexDirection: "row",
|
|
28
|
+
justifyContent: "center",
|
|
29
|
+
alignItems: "center",
|
|
30
|
+
minHeight: 50,
|
|
31
|
+
width: "100%",
|
|
32
|
+
// Add padding so the border doesn't mess up layouts
|
|
33
|
+
paddingHorizontal: 6,
|
|
34
|
+
paddingVertical: 4,
|
|
35
|
+
borderColor: Unifier.theme.gray,
|
|
36
|
+
borderWidth: 1,
|
|
37
|
+
borderRadius: 5,
|
|
38
|
+
backgroundColor: Unifier.theme.white,
|
|
39
|
+
},
|
|
40
|
+
}}
|
|
41
|
+
value={value}
|
|
42
|
+
onValueChange={onChange}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
49
45
|
}
|
package/src/SplitPage.tsx
CHANGED
|
@@ -3,9 +3,7 @@ import {ListRenderItemInfo, View} from "react-native";
|
|
|
3
3
|
|
|
4
4
|
import {Box} from "./Box";
|
|
5
5
|
import {Color} from "./Common";
|
|
6
|
-
import {ErrorBoundary} from "./ErrorBoundary";
|
|
7
6
|
import {FlatList} from "./FlatList";
|
|
8
|
-
import {Icon} from "./Icon";
|
|
9
7
|
import {IconButton} from "./IconButton";
|
|
10
8
|
import {mediaQueryLargerThan} from "./MediaQuery";
|
|
11
9
|
import {Spinner} from "./Spinner";
|
package/src/TapToEdit.tsx
CHANGED
|
@@ -1,48 +1,118 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, {ReactElement, useState} from "react";
|
|
2
2
|
|
|
3
3
|
import {Box} from "./Box";
|
|
4
4
|
import {Button} from "./Button";
|
|
5
|
-
import {
|
|
5
|
+
import {BoxProps} from "./Common";
|
|
6
|
+
import {Field, FieldProps} from "./Field";
|
|
6
7
|
import {Icon} from "./Icon";
|
|
7
8
|
import {Text} from "./Text";
|
|
8
|
-
import {TextField} from "./TextField";
|
|
9
9
|
|
|
10
|
-
interface
|
|
11
|
-
|
|
10
|
+
export interface TapToEditProps extends Omit<FieldProps, "handleChange"> {
|
|
11
|
+
title: string;
|
|
12
|
+
// Not required if not editable.
|
|
13
|
+
onSave?: (value: any) => void | Promise<void>;
|
|
14
|
+
// Defaults to true
|
|
15
|
+
editable?: boolean;
|
|
16
|
+
// For changing how the non-editing row renders
|
|
17
|
+
rowBoxProps?: Partial<BoxProps>;
|
|
18
|
+
transform?: (value: any) => string;
|
|
19
|
+
fieldComponent?: (setValue: () => void) => ReactElement;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
|
-
export
|
|
15
|
-
|
|
22
|
+
export const TapToEdit = (props: TapToEditProps): ReactElement => {
|
|
23
|
+
const [editing, setEditing] = useState(false);
|
|
24
|
+
const [value, setValue] = useState(props.initialValue);
|
|
16
25
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
26
|
+
const {title, editable = true, rowBoxProps, transform, fieldComponent, ...fieldProps} = props;
|
|
27
|
+
if (editing) {
|
|
28
|
+
return (
|
|
29
|
+
<Box direction="column" maxWidth="100%" paddingX={3} paddingY={2} width="100%">
|
|
30
|
+
{fieldComponent ? (
|
|
31
|
+
fieldComponent(setValue as any)
|
|
32
|
+
) : (
|
|
33
|
+
<Field
|
|
34
|
+
handleChange={(_: string, val: any): void => setValue(val)}
|
|
35
|
+
label={props.title}
|
|
36
|
+
{...fieldProps}
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
<Box direction="row">
|
|
40
|
+
<Button
|
|
41
|
+
color="blue"
|
|
42
|
+
inline
|
|
43
|
+
text="Save"
|
|
44
|
+
onClick={async (): Promise<void> => {
|
|
45
|
+
if (!props.onSave) {
|
|
46
|
+
console.error("No onSave provided for editable TapToEdit");
|
|
47
|
+
} else {
|
|
48
|
+
await props.onSave(value);
|
|
49
|
+
}
|
|
50
|
+
setEditing(false);
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
<Box marginLeft={2}>
|
|
32
54
|
<Button
|
|
33
|
-
color="
|
|
55
|
+
color="red"
|
|
34
56
|
inline
|
|
35
|
-
text="
|
|
36
|
-
onClick={() => {
|
|
37
|
-
|
|
38
|
-
if (this.props.onSubmitEditing) {
|
|
39
|
-
this.props.onSubmitEditing();
|
|
40
|
-
}
|
|
57
|
+
text="Cancel"
|
|
58
|
+
onClick={(): void => {
|
|
59
|
+
setEditing(false);
|
|
41
60
|
}}
|
|
42
61
|
/>
|
|
43
62
|
</Box>
|
|
44
63
|
</Box>
|
|
45
|
-
|
|
64
|
+
</Box>
|
|
65
|
+
);
|
|
66
|
+
} else {
|
|
67
|
+
let displayValue = value;
|
|
68
|
+
// If transform is present, that takes priority
|
|
69
|
+
if (transform) {
|
|
70
|
+
displayValue = transform(value);
|
|
71
|
+
} else {
|
|
72
|
+
// If no transform, try and display the value reasonably.
|
|
73
|
+
if (fieldProps?.type === "boolean") {
|
|
74
|
+
displayValue = value ? "Yes" : "No";
|
|
75
|
+
} else if (fieldProps?.type === "percent") {
|
|
76
|
+
// Prevent floating point errors from showing up by using parseFloat and precision. Pass through parseFloat again
|
|
77
|
+
// to trim off insignificant zeroes.
|
|
78
|
+
displayValue = `${parseFloat(parseFloat(String(value * 100)).toPrecision(7))}%`;
|
|
79
|
+
} else if (fieldProps?.type === "currency") {
|
|
80
|
+
// TODO: support currencies other than USD in Field and related components.
|
|
81
|
+
const formatter = new Intl.NumberFormat("en-US", {
|
|
82
|
+
style: "currency",
|
|
83
|
+
currency: "USD",
|
|
84
|
+
minimumFractionDigits: 2, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
|
|
85
|
+
});
|
|
86
|
+
displayValue = formatter.format(value);
|
|
87
|
+
} else if (fieldProps?.type === "multiselect") {
|
|
88
|
+
// ???
|
|
89
|
+
displayValue = value.join(", ");
|
|
90
|
+
}
|
|
46
91
|
}
|
|
92
|
+
return (
|
|
93
|
+
<Box
|
|
94
|
+
direction="row"
|
|
95
|
+
justifyContent="between"
|
|
96
|
+
maxWidth="100%"
|
|
97
|
+
paddingX={3}
|
|
98
|
+
paddingY={2}
|
|
99
|
+
width="100%"
|
|
100
|
+
{...rowBoxProps}
|
|
101
|
+
>
|
|
102
|
+
<Box>
|
|
103
|
+
<Text weight="bold">{title}:</Text>
|
|
104
|
+
</Box>
|
|
105
|
+
<Box direction="row" flex="shrink" marginLeft={2}>
|
|
106
|
+
<Box flex="shrink">
|
|
107
|
+
<Text overflow="breakWord">{displayValue}</Text>
|
|
108
|
+
</Box>
|
|
109
|
+
{editable && (
|
|
110
|
+
<Box marginLeft={2} onClick={(): void => setEditing(true)}>
|
|
111
|
+
<Icon color="darkGray" name="edit" size="lg" />
|
|
112
|
+
</Box>
|
|
113
|
+
)}
|
|
114
|
+
</Box>
|
|
115
|
+
</Box>
|
|
116
|
+
);
|
|
47
117
|
}
|
|
48
|
-
}
|
|
118
|
+
};
|