-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0e0cecd
commit 9f79c21
Showing
6 changed files
with
4,870 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
import * as React from "react"; | ||
import { View, ViewStyle, Text, StyleSheet } from "react-native"; | ||
import type { ControlStyle } from "../../types/ControlStyle"; | ||
import { useRefresh } from "../../hooks/useRefresh"; | ||
import { Hitbox } from "../Hitbox"; | ||
import { SimpleModal } from "../SimpleModal"; | ||
import { | ||
createControlPlaceholderTextStyleInstance, | ||
createControlStateStyleInstance, | ||
createControlStyleInstance, | ||
createControlTextStyleInstance, | ||
createFullHeightPopoverStateStyleInstance, | ||
} from "../helpers"; | ||
|
||
/** | ||
* Creates a new React component which displays a button which can be pressed to | ||
* show an element in a pop-over which fills the display vertically. | ||
* @param controlStyle The styling to use. | ||
* @returns The created React component. | ||
*/ | ||
export const createFullHeightPopoverComponent = ( | ||
controlStyle: ControlStyle | ||
): React.FunctionComponent<{ | ||
/** | ||
* The text shown in the button. When null, the placeholder is shown instead. | ||
*/ | ||
readonly label: null | string; | ||
|
||
/** | ||
* The placeholder text shown in the button when there is no text. | ||
*/ | ||
readonly placeholder: string; | ||
|
||
/** | ||
* When true, the button cannot be pressed and the body is not shown. | ||
*/ | ||
readonly disabled: boolean; | ||
|
||
/** | ||
* When true, the control is styled as though it is valid. It is otherwise | ||
* styles as though it is invalid. | ||
*/ | ||
readonly valid: boolean; | ||
}> => { | ||
const styles = StyleSheet.create({ | ||
blurredValidHitbox: createControlStyleInstance( | ||
controlStyle, | ||
controlStyle.blurredValid | ||
), | ||
blurredInvalidHitbox: createControlStateStyleInstance( | ||
controlStyle, | ||
controlStyle.blurredInvalid | ||
), | ||
focusedValidHitbox: createControlStateStyleInstance( | ||
controlStyle, | ||
controlStyle.focusedValid | ||
), | ||
focusedInvalidHitbox: createControlStateStyleInstance( | ||
controlStyle, | ||
controlStyle.focusedInvalid | ||
), | ||
disabledValidHitbox: createControlStateStyleInstance( | ||
controlStyle, | ||
controlStyle.disabledValid | ||
), | ||
disabledInvalidHitbox: createControlStateStyleInstance( | ||
controlStyle, | ||
controlStyle.disabledInvalid | ||
), | ||
disabledValidText: createControlTextStyleInstance( | ||
controlStyle, | ||
controlStyle.disabledValid | ||
), | ||
disabledInvalidText: createControlTextStyleInstance( | ||
controlStyle, | ||
controlStyle.disabledInvalid | ||
), | ||
blurredValidText: createControlTextStyleInstance( | ||
controlStyle, | ||
controlStyle.blurredValid | ||
), | ||
blurredInvalidText: createControlTextStyleInstance( | ||
controlStyle, | ||
controlStyle.blurredInvalid | ||
), | ||
focusedValidText: createControlTextStyleInstance( | ||
controlStyle, | ||
controlStyle.focusedValid | ||
), | ||
focusedInvalidText: createControlTextStyleInstance( | ||
controlStyle, | ||
controlStyle.focusedInvalid | ||
), | ||
disabledValidPlaceholderText: createControlPlaceholderTextStyleInstance( | ||
controlStyle, | ||
controlStyle.disabledValid | ||
), | ||
disabledInvalidPlaceholderText: createControlPlaceholderTextStyleInstance( | ||
controlStyle, | ||
controlStyle.disabledInvalid | ||
), | ||
blurredValidPlaceholderText: createControlPlaceholderTextStyleInstance( | ||
controlStyle, | ||
controlStyle.blurredValid | ||
), | ||
blurredInvalidPlaceholderText: createControlPlaceholderTextStyleInstance( | ||
controlStyle, | ||
controlStyle.blurredInvalid | ||
), | ||
focusedValidPlaceholderText: createControlPlaceholderTextStyleInstance( | ||
controlStyle, | ||
controlStyle.focusedValid | ||
), | ||
focusedInvalidPlaceholderText: createControlPlaceholderTextStyleInstance( | ||
controlStyle, | ||
controlStyle.focusedInvalid | ||
), | ||
validView: createFullHeightPopoverStateStyleInstance( | ||
controlStyle.focusedValid | ||
), | ||
invalidView: createFullHeightPopoverStateStyleInstance( | ||
controlStyle.focusedInvalid | ||
), | ||
}); | ||
|
||
return ({ label, placeholder, disabled, valid, children }) => { | ||
const refresh = useRefresh(); | ||
|
||
const state = React.useRef<{ | ||
open: boolean; | ||
layout: null | { | ||
readonly pageX: number; | ||
readonly width: number; | ||
}; | ||
}>({ | ||
open: false, | ||
layout: null, | ||
}); | ||
|
||
// Ensure that the drop-down does not re-open itself if it is disabled while | ||
// open, then re-enabled. | ||
if (disabled) { | ||
state.current.open = false; | ||
} | ||
|
||
let additionalModalViewStyle: null | ViewStyle; | ||
|
||
if (!disabled && state.current.open && state.current.layout !== null) { | ||
additionalModalViewStyle = { | ||
left: state.current.layout.pageX, | ||
width: state.current.layout.width, | ||
}; | ||
} else { | ||
additionalModalViewStyle = null; | ||
} | ||
|
||
const inline = ( | ||
<Hitbox | ||
style={ | ||
disabled | ||
? valid | ||
? styles.disabledValidHitbox | ||
: styles.disabledInvalidHitbox | ||
: additionalModalViewStyle === null | ||
? valid | ||
? styles.blurredValidHitbox | ||
: styles.blurredInvalidHitbox | ||
: valid | ||
? styles.focusedValidHitbox | ||
: styles.focusedInvalidHitbox | ||
} | ||
onMeasure={(x, y, width, height, pageX, pageY) => { | ||
x; | ||
y; | ||
height; | ||
pageY; | ||
|
||
if ( | ||
state.current.layout === null || | ||
pageX !== state.current.layout.pageX || | ||
width !== state.current.layout.width | ||
) { | ||
state.current.layout = { | ||
pageX, | ||
width, | ||
}; | ||
|
||
refresh(); | ||
} | ||
}} | ||
onPress={() => { | ||
state.current.open = true; | ||
|
||
refresh(); | ||
}} | ||
disabled={disabled} | ||
> | ||
<Text | ||
style={ | ||
disabled | ||
? valid | ||
? label === null | ||
? styles.disabledValidPlaceholderText | ||
: styles.disabledValidText | ||
: label === null | ||
? styles.disabledInvalidPlaceholderText | ||
: styles.disabledInvalidText | ||
: additionalModalViewStyle === null | ||
? valid | ||
? label === null | ||
? styles.blurredValidPlaceholderText | ||
: styles.blurredValidText | ||
: label === null | ||
? styles.blurredInvalidPlaceholderText | ||
: styles.blurredInvalidText | ||
: valid | ||
? label === null | ||
? styles.focusedValidPlaceholderText | ||
: styles.focusedValidText | ||
: label === null | ||
? styles.focusedInvalidPlaceholderText | ||
: styles.focusedInvalidText | ||
} | ||
> | ||
{label ?? placeholder} | ||
</Text> | ||
</Hitbox> | ||
); | ||
|
||
if (additionalModalViewStyle === null) { | ||
return inline; | ||
} else { | ||
return ( | ||
<React.Fragment> | ||
{inline} | ||
<SimpleModal | ||
onClose={() => { | ||
state.current.open = false; | ||
|
||
refresh(); | ||
}} | ||
> | ||
<View | ||
style={[ | ||
valid ? styles.validView : styles.invalidView, | ||
additionalModalViewStyle, | ||
]} | ||
> | ||
{children} | ||
</View> | ||
</SimpleModal> | ||
</React.Fragment> | ||
); | ||
} | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# `react-native-app-helpers/createFullHeightPopoverComponent` | ||
|
||
Creates a new React component which displays a button which can be pressed to | ||
show an element in a pop-over which fills the display vertically. | ||
|
||
This is particularly useful for searchable and creatable selects, where the | ||
button may be obscured by the keyboard (and as much vertical height as possible | ||
is desirable for the list of options). | ||
|
||
## Usage | ||
|
||
```tsx | ||
import { createFullHeightPopoverComponent } from "react-native-app-helpers"; | ||
|
||
const ExampleFullHeightPopover = createDropDownComponent( | ||
{ | ||
fontFamily: `Example Font Family`, | ||
fontSize: 37, | ||
paddingVertical: 12, | ||
paddingHorizontal: 29, | ||
blurredValid: { | ||
textColor: `#FFEE00`, | ||
placeholderColor: `#E7AA32`, | ||
backgroundColor: `#32AE12`, | ||
radius: 5, | ||
border: { | ||
width: 4, | ||
color: `#FF00FF`, | ||
}, | ||
}, | ||
blurredInvalid: { | ||
textColor: `#99FE88`, | ||
placeholderColor: `#CACA3A`, | ||
backgroundColor: `#259284`, | ||
radius: 10, | ||
border: { | ||
width: 6, | ||
color: `#9A9A8E`, | ||
}, | ||
}, | ||
focusedValid: { | ||
textColor: `#55EA13`, | ||
placeholderColor: `#273346`, | ||
backgroundColor: `#CABA99`, | ||
radius: 3, | ||
border: { | ||
width: 5, | ||
color: `#646464`, | ||
}, | ||
}, | ||
focusedInvalid: { | ||
textColor: `#ABAADE`, | ||
placeholderColor: `#47ADAD`, | ||
backgroundColor: `#32AA88`, | ||
radius: 47, | ||
border: { | ||
width: 12, | ||
color: `#98ADAA`, | ||
}, | ||
}, | ||
disabledValid: { | ||
textColor: `#AE2195`, | ||
placeholderColor: `#FFAAEE`, | ||
backgroundColor: `#772728`, | ||
radius: 100, | ||
border: { | ||
width: 14, | ||
color: `#5E5E5E`, | ||
}, | ||
}, | ||
disabledInvalid: { | ||
textColor: `#340297`, | ||
placeholderColor: `#233832`, | ||
backgroundColor: `#938837`, | ||
radius: 2, | ||
border: { | ||
width: 19, | ||
color: `#573829`, | ||
}, | ||
}, | ||
} | ||
); | ||
|
||
const ExampleScreen = () => ( | ||
<ExampleFullHeightPopover | ||
button={<Text>Click or touch to open the pop-over.</Text>} | ||
body={(position) => ( | ||
<Text> | ||
This is shown in a column spanning the full height of the display when | ||
the button is pressed. | ||
</Text> | ||
)} | ||
disabled={false} | ||
/> | ||
); | ||
``` |
Oops, something went wrong.