Skip to content

Commit f822950

Browse files
committed
Add flash messages.
1 parent ff44708 commit f822950

File tree

9 files changed

+1446
-0
lines changed

9 files changed

+1446
-0
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import * as React from "react";
2+
import { StyleSheet, Text, TextStyle, ViewStyle } from "react-native";
3+
import { useRefresh } from "../../hooks/useRefresh";
4+
import type { FlashMessageState } from "../../types/FlashMessageState";
5+
import type { FlashMessageStyle } from "../../types/FlashMessageStyle";
6+
import { Hitbox } from "../Hitbox";
7+
8+
/**
9+
* Creates a new React component representing a flash message.
10+
* @template T The names of the types available.
11+
* @param flashMessageStyle The style of the flash message to create.
12+
* @returns The created React component.
13+
*/
14+
export function createFlashMessageComponent<T extends string>(
15+
flashMessageStyle: FlashMessageStyle<T>
16+
): React.FunctionComponent<{
17+
/**
18+
* When null, the flash message is closed. When non-null, the details of the
19+
* flash message which may be displayed (the user can dismiss the current
20+
* message).
21+
*/
22+
readonly state: null | FlashMessageState<T>;
23+
}> {
24+
const hitboxStylesInput: { [TKey in T]?: ViewStyle } = {};
25+
const textStylesInput: { [TKey in T]?: TextStyle } = {};
26+
27+
for (const typeKey in flashMessageStyle.types) {
28+
const typeValue = flashMessageStyle.types[typeKey];
29+
30+
const hitboxStyle: ViewStyle = {
31+
backgroundColor: typeValue.backgroundColor,
32+
};
33+
34+
if (typeValue.border !== null) {
35+
hitboxStyle.borderWidth = typeValue.border.width;
36+
hitboxStyle.borderColor = typeValue.border.color;
37+
}
38+
39+
if (flashMessageStyle.horizontalPadding !== 0) {
40+
hitboxStyle.paddingHorizontal = flashMessageStyle.horizontalPadding;
41+
}
42+
43+
if (flashMessageStyle.verticalPadding !== 0) {
44+
hitboxStyle.paddingVertical = flashMessageStyle.verticalPadding;
45+
}
46+
47+
if (flashMessageStyle.radius !== 0) {
48+
hitboxStyle.borderRadius = flashMessageStyle.radius;
49+
}
50+
51+
hitboxStylesInput[typeKey] = hitboxStyle;
52+
53+
const textStyle: TextStyle = {
54+
fontFamily: flashMessageStyle.fontFamily,
55+
fontSize: flashMessageStyle.fontSize,
56+
lineHeight: flashMessageStyle.fontSize * 1.4,
57+
color: typeValue.color,
58+
};
59+
60+
textStylesInput[typeKey] = textStyle;
61+
}
62+
63+
const hitboxStyles = StyleSheet.create(
64+
hitboxStylesInput as { readonly [TKey in T]: ViewStyle }
65+
);
66+
67+
const textStyles = StyleSheet.create(
68+
textStylesInput as { readonly [TKey in T]: TextStyle }
69+
);
70+
71+
return ({ state }) => {
72+
const refresh = useRefresh();
73+
const internalState = React.useRef({
74+
open: state !== null,
75+
type: state === null ? null : state.type,
76+
message: state === null ? null : state.message,
77+
});
78+
79+
if (state === null) {
80+
internalState.current.open = false;
81+
internalState.current.type = null;
82+
internalState.current.message = null;
83+
} else if (
84+
state.type !== internalState.current.type ||
85+
state.message !== internalState.current.message
86+
) {
87+
internalState.current.open = true;
88+
internalState.current.type = state.type;
89+
internalState.current.message = state.message;
90+
}
91+
92+
if (state !== null && internalState.current.open) {
93+
return (
94+
<Hitbox
95+
disabled={false}
96+
onPress={() => {
97+
console.log("let us go!");
98+
internalState.current.open = false;
99+
refresh();
100+
}}
101+
style={hitboxStyles[state.type]}
102+
>
103+
<Text style={textStyles[state.type]}>{state.message}</Text>
104+
</Hitbox>
105+
);
106+
} else {
107+
return null;
108+
}
109+
};
110+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# `react-native-app-helpers/createFlashMessageComponent`
2+
3+
Creates a new React component representing a flash message.
4+
5+
## Usage
6+
7+
```tsx
8+
import { createFlashMessageComponent, FlashMessageState } from "react-native-app-helpers";
9+
import { Button } from "react-native";
10+
11+
const ExampleFlashMessage = createFlashMessageComponent(
12+
{
13+
fontFamily: `Example Font Family`,
14+
fontSize: 25,
15+
radius: 12,
16+
horizontalPadding: 41,
17+
verticalPadding: 57,
18+
types: {
19+
exampleTypeA: {
20+
backgroundColor: `red`,
21+
color: `green`,
22+
border: null,
23+
},
24+
exampleTypeB: {
25+
backgroundColor: `blue`,
26+
color: `yellow`,
27+
border: {
28+
width: 15,
29+
color: `orange`,
30+
},
31+
},
32+
exampleTypeC: {
33+
backgroundColor: `purple`,
34+
color: `cyan`,
35+
border: null,
36+
},
37+
exampleTypeD: {
38+
backgroundColor: `magenta`,
39+
color: `black`,
40+
border: {
41+
width: 24,
42+
color: `white`,
43+
},
44+
},
45+
},
46+
}
47+
);
48+
49+
const ExampleScreen = () => {
50+
const [state, setState] = React.useState<null | FlashMessageState<`exampleTypeA` | `exampleTypeB` | `exampleTypeC` | `exampleTypeD`>>(null);
51+
52+
return (
53+
<React.Fragment>
54+
<ExampleFlashMessage state={state} />
55+
<Button
56+
onPress={() => {
57+
setState({
58+
type: `exampleTypeB`,
59+
message: `This is example message B.`,
60+
})
61+
}}
62+
title="Show Message B"
63+
/>
64+
<Button
65+
onPress={() => {
66+
setState({
67+
type: `exampleTypeC`,
68+
message: `This is example message C.`,
69+
})
70+
}}
71+
title="Show Message C"
72+
/>
73+
</React.Fragment>
74+
);
75+
};
76+
```

0 commit comments

Comments
 (0)