| author | |
| committer | |
| log | abc80c80a73fdda78600184276da9e36cca89703 |
| tree | 81bbd28c12b0ff8bfc81e8e8ed7a2355380bb3a0 |
| parent | 9a2b9a2d4040c7e78c03a95d59b76b92e6bf2957 |
| signature | Signed by PGP key B5690EEEBB952194 |
3 files changed, 64 insertions(+), 43 deletions(-)
apps/mobile/src/components/ListChoiceRow.tsx created+60| ... | ... | @@ -0,0 +1,60 @@ |
| 1 | import { Platform, View } from "react-native"; | |
| 2 | import { useResolveClassNames } from "uniwind"; | |
| 3 | ||
| 4 | import { cn } from "../lib/cn"; | |
| 5 | import { SymbolView } from "./AppSymbol"; | |
| 6 | import { ListRowLayout } from "./ListRowLayout"; | |
| 7 | import { MaterialRadioIndicator } from "./MaterialRadioIndicator"; | |
| 8 | import { RowPressable } from "./RowPressable"; | |
| 9 | ||
| 10 | export function ListChoiceRow(props: { | |
| 11 | readonly label: string; | |
| 12 | readonly description?: string; | |
| 13 | readonly selected: boolean; | |
| 14 | readonly separated?: boolean; | |
| 15 | readonly disabled?: boolean; | |
| 16 | readonly onPress: () => void; | |
| 17 | }) { | |
| 18 | const rippleStyle = useResolveClassNames("bg-subtle-strong"); | |
| 19 | return ( | |
| 20 | <RowPressable | |
| 21 | accessibilityRole="radio" | |
| 22 | accessibilityLabel={props.label} | |
| 23 | accessibilityHint={props.description} | |
| 24 | accessibilityState={{ checked: props.selected, disabled: Boolean(props.disabled) }} | |
| 25 | disabled={props.disabled} | |
| 26 | onPress={props.onPress} | |
| 27 | android_ripple={{ color: rippleStyle.backgroundColor }} | |
| 28 | unstable_pressDelay={Platform.OS === "android" ? 50 : undefined} | |
| 29 | className={cn( | |
| 30 | props.selected ? "bg-subtle" : "bg-grouped-card", | |
| 31 | props.separated && "border-t border-border-subtle", | |
| 32 | )} | |
| 33 | > | |
| 34 | <ListRowLayout | |
| 35 | label={props.label} | |
| 36 | description={props.description} | |
| 37 | disabled={props.disabled} | |
| 38 | accessory={ | |
| 39 | <View | |
| 40 | accessibilityElementsHidden | |
| 41 | importantForAccessibility="no-hide-descendants" | |
| 42 | className="size-6 items-center justify-center" | |
| 43 | > | |
| 44 | {Platform.OS === "android" ? ( | |
| 45 | <MaterialRadioIndicator selected={props.selected} /> | |
| 46 | ) : props.selected ? ( | |
| 47 | <SymbolView | |
| 48 | name="checkmark" | |
| 49 | size={18} | |
| 50 | tintColorClassName="accent-primary-text" | |
| 51 | type="monochrome" | |
| 52 | weight="semibold" | |
| 53 | /> | |
| 54 | ) : null} | |
| 55 | </View> | |
| 56 | } | |
| 57 | /> | |
| 58 | </RowPressable> | |
| 59 | ); | |
| 60 | } |
apps/mobile/src/components/MaterialRadioIndicator.android.tsx+3-2| ... | ... | @@ -22,7 +22,8 @@ export function MaterialRadioIndicator({ selected }: { readonly selected: boolea |
| 22 | 22 | height: 20, |
| 23 | 23 | borderRadius: 10, |
| 24 | 24 | borderWidth: 2, |
| 25 | borderColor: themeVariables[selected ? "--color-primary" : "--color-foreground-muted"], | |
| 25 | borderColor: | |
| 26 | themeVariables[selected ? "--color-primary-text" : "--color-foreground-muted"], | |
| 26 | 27 | alignItems: "center", |
| 27 | 28 | justifyContent: "center", |
| 28 | 29 | }} |
| ... | ... | @@ -33,7 +34,7 @@ export function MaterialRadioIndicator({ selected }: { readonly selected: boolea |
| 33 | 34 | width: 10, |
| 34 | 35 | height: 10, |
| 35 | 36 | borderRadius: 5, |
| 36 | backgroundColor: themeVariables["--color-primary"], | |
| 37 | backgroundColor: themeVariables["--color-primary-text"], | |
| 37 | 38 | }} |
| 38 | 39 | /> |
| 39 | 40 | ) : null} |
apps/mobile/src/features/settings/components/SettingsChoiceRow.tsx+1-41| ... | ... | @@ -1,41 +1 @@ |
| 1 | import { Pressable, View } from "react-native"; | |
| 2 | ||
| 3 | import { AppText as Text } from "../../../components/AppText"; | |
| 4 | import { SymbolView } from "../../../components/AppSymbol"; | |
| 5 | ||
| 6 | export function SettingsChoiceRow(props: { | |
| 7 | readonly label: string; | |
| 8 | readonly description: string; | |
| 9 | readonly selected: boolean; | |
| 10 | readonly separated: boolean; | |
| 11 | readonly disabled: boolean; | |
| 12 | readonly onPress: () => void; | |
| 13 | }) { | |
| 14 | return ( | |
| 15 | <Pressable | |
| 16 | accessibilityRole="radio" | |
| 17 | accessibilityState={{ checked: props.selected, disabled: props.disabled }} | |
| 18 | className={ | |
| 19 | props.separated | |
| 20 | ? "flex-row items-center gap-4 border-t border-border-subtle p-4 active:opacity-70" | |
| 21 | : "flex-row items-center gap-4 p-4 active:opacity-70" | |
| 22 | } | |
| 23 | disabled={props.disabled} | |
| 24 | onPress={props.onPress} | |
| 25 | > | |
| 26 | <View className="min-w-0 flex-1 gap-1"> | |
| 27 | <Text className="text-lg text-foreground android:text-base">{props.label}</Text> | |
| 28 | <Text className="text-sm leading-normal text-foreground-muted">{props.description}</Text> | |
| 29 | </View> | |
| 30 | {props.selected ? ( | |
| 31 | <SymbolView | |
| 32 | name="checkmark" | |
| 33 | size={18} | |
| 34 | tintColorClassName="accent-icon" | |
| 35 | type="monochrome" | |
| 36 | weight="semibold" | |
| 37 | /> | |
| 38 | ) : null} | |
| 39 | </Pressable> | |
| 40 | ); | |
| 41 | } | |
| 1 | export { ListChoiceRow as SettingsChoiceRow } from "../../../components/ListChoiceRow"; |