| author | |
| committer | |
| log | 9a2b9a2d4040c7e78c03a95d59b76b92e6bf2957 |
| tree | 4151df94449e4e56bb398509f4a7ac96d267a18b |
| parent | a39fd7be954379dfedab0f1d1e2fd8c02d748e9b |
| signature | Signed by PGP key B5690EEEBB952194 |
3 files changed, 53 insertions(+), 77 deletions(-)
apps/mobile/src/components/ListToggleRow.tsx created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | import { Pressable } from "react-native"; | |
| 2 | ||
| 3 | import { AppText } from "./AppText"; | |
| 4 | import { ListRowLayout, type ListRowContentProps } from "./ListRowLayout"; | |
| 5 | import { ThemedSwitch } from "./ThemedSwitch"; | |
| 6 | ||
| 7 | /** A null value means the selected environments disagree; activation sets them all on. */ | |
| 8 | export function ListToggleRow( | |
| 9 | props: Omit<ListRowContentProps, "detail"> & { | |
| 10 | readonly value: boolean | null; | |
| 11 | readonly onValueChange: (value: boolean) => void; | |
| 12 | }, | |
| 13 | ) { | |
| 14 | return ( | |
| 15 | <ListRowLayout | |
| 16 | icon={props.icon} | |
| 17 | label={props.label} | |
| 18 | description={props.description} | |
| 19 | disabled={props.disabled} | |
| 20 | accessory={ | |
| 21 | props.value === null ? ( | |
| 22 | <Pressable | |
| 23 | accessibilityLabel={`Set ${props.label} on for selected environments`} | |
| 24 | accessibilityRole="button" | |
| 25 | accessibilityHint={props.description} | |
| 26 | accessibilityState={{ disabled: Boolean(props.disabled) }} | |
| 27 | disabled={props.disabled} | |
| 28 | className="rounded-full bg-subtle px-3 py-2 active:opacity-70" | |
| 29 | onPress={() => props.onValueChange(true)} | |
| 30 | > | |
| 31 | <AppText className="text-sm font-t3-medium text-foreground">Mixed · Set on</AppText> | |
| 32 | </Pressable> | |
| 33 | ) : ( | |
| 34 | <ThemedSwitch | |
| 35 | accessibilityLabel={props.label} | |
| 36 | accessibilityHint={props.description} | |
| 37 | disabled={props.disabled} | |
| 38 | onValueChange={props.onValueChange} | |
| 39 | value={props.value} | |
| 40 | /> | |
| 41 | ) | |
| 42 | } | |
| 43 | /> | |
| 44 | ); | |
| 45 | } |
apps/mobile/src/features/settings/components/SettingsControlRow.tsx deleted-39| ... | ... | @@ -1,39 +0,0 @@ |
| 1 | import type { ComponentProps, ReactNode } from "react"; | |
| 2 | import { Platform, View } from "react-native"; | |
| 3 | ||
| 4 | import { SymbolView } from "../../../components/AppSymbol"; | |
| 5 | import { AppText as Text } from "../../../components/AppText"; | |
| 6 | import { cn } from "../../../lib/cn"; | |
| 7 | ||
| 8 | export function SettingsControlRow(props: { | |
| 9 | readonly disabled?: boolean; | |
| 10 | readonly icon: ComponentProps<typeof SymbolView>["name"]; | |
| 11 | readonly label: string; | |
| 12 | readonly subtitle?: string; | |
| 13 | readonly children: ReactNode; | |
| 14 | }) { | |
| 15 | return ( | |
| 16 | <View | |
| 17 | className={cn( | |
| 18 | "flex-row items-center gap-4 p-4 android:min-h-14 android:py-3", | |
| 19 | props.subtitle && "android:min-h-18", | |
| 20 | props.disabled && "opacity-[0.45]", | |
| 21 | )} | |
| 22 | > | |
| 23 | <SymbolView | |
| 24 | name={props.icon} | |
| 25 | size={Platform.OS === "android" ? 24 : 22} | |
| 26 | tintColorClassName="accent-icon" | |
| 27 | type="monochrome" | |
| 28 | weight="regular" | |
| 29 | /> | |
| 30 | <View className="min-w-0 flex-1 android:gap-1"> | |
| 31 | <Text className="text-lg text-foreground android:text-base">{props.label}</Text> | |
| 32 | {props.subtitle ? ( | |
| 33 | <Text className="text-sm text-foreground-muted">{props.subtitle}</Text> | |
| 34 | ) : null} | |
| 35 | </View> | |
| 36 | {props.children} | |
| 37 | </View> | |
| 38 | ); | |
| 39 | } |
apps/mobile/src/features/settings/components/SettingsSwitchRow.tsx+8-38| ... | ... | @@ -1,42 +1,12 @@ |
| 1 | 1 | import type { ComponentProps } from "react"; |
| 2 | import { Pressable } from "react-native"; | |
| 3 | 2 | |
| 4 | import { AppText as Text } from "../../../components/AppText"; | |
| 5 | import { ThemedSwitch } from "../../../components/ThemedSwitch"; | |
| 6 | import { SettingsControlRow } from "./SettingsControlRow"; | |
| 3 | import { ListToggleRow } from "../../../components/ListToggleRow"; | |
| 7 | 4 | |
| 8 | export function SettingsSwitchRow( | |
| 9 | props: Omit<ComponentProps<typeof SettingsControlRow>, "children"> & { | |
| 10 | readonly value: boolean | null; | |
| 11 | readonly onValueChange: (value: boolean) => void; | |
| 12 | }, | |
| 13 | ) { | |
| 14 | return ( | |
| 15 | <SettingsControlRow | |
| 16 | disabled={props.disabled} | |
| 17 | icon={props.icon} | |
| 18 | label={props.label} | |
| 19 | subtitle={props.subtitle} | |
| 20 | > | |
| 21 | {props.value === null ? ( | |
| 22 | <Pressable | |
| 23 | accessibilityLabel={`Set ${props.label} on for selected environments`} | |
| 24 | accessibilityRole="button" | |
| 25 | disabled={props.disabled} | |
| 26 | className="rounded-full bg-subtle px-3 py-2 active:opacity-70" | |
| 27 | onPress={() => props.onValueChange(true)} | |
| 28 | > | |
| 29 | <Text className="text-sm font-t3-medium text-foreground">Mixed · Set on</Text> | |
| 30 | </Pressable> | |
| 31 | ) : ( | |
| 32 | <ThemedSwitch | |
| 33 | style={{ alignSelf: "center" }} | |
| 34 | accessibilityLabel={props.label} | |
| 35 | disabled={props.disabled} | |
| 36 | onValueChange={props.onValueChange} | |
| 37 | value={props.value} | |
| 38 | /> | |
| 39 | )} | |
| 40 | </SettingsControlRow> | |
| 41 | ); | |
| 5 | export function SettingsSwitchRow({ | |
| 6 | subtitle, | |
| 7 | ...props | |
| 8 | }: Omit<ComponentProps<typeof ListToggleRow>, "description"> & { | |
| 9 | readonly subtitle?: string; | |
| 10 | }) { | |
| 11 | return <ListToggleRow {...props} description={subtitle} />; | |
| 42 | 12 | } |