Files
orca/mobile/src/browser/MobileBrowserViewModeSwitch.tsx
T
Kelvin Amoaba a079f51991 feat(mobile): unify corner radii across the app (#36437)
* feat(mobile): give every corner one of three shapes

Corners had drifted to 17 different radii, so a screen mixed 6 pt
buttons, 14 pt cards, 12 pt groups and 4 pt badges. Every corner now
comes from the shape of what it is:

- control (12): anything you tap — buttons, icon buttons, fields,
  pickers, segmented controls, key caps.
- surface (16): anything that holds content — cards, groups, sheets,
  banners, code boxes. Dialogs and the composer box use floating (24).
- pill: badges, chips, status pills, dots, avatars and the composer's
  own round buttons.
- tile (10) for icon squares and mark (4) for inline code and check
  squares; nested shapes take the parent's radius minus the gap.

Every rounded style also asks for the continuous (squircle) corner
curve; iOS draws it, Android and the web ignore it.

* fix(mobile): keep circles and pills on the circular corner curve

The continuous curve squares off a corner radius that reaches half the element's size, so the FAB, dots, avatars and the composer's round buttons stopped being round on iOS.

* fix(mobile): put the anchored detail card on the surface radius

* fix(mobile): keep the circular curve on unclipped bordered styles

React Native strokes an unclipped visible border on a circular path while it fills the background with the continuous curve, so the two edges drifted apart at the corners.
2026-10-30 21:27:30 +00:00

113 lines
2.7 KiB
TypeScript

import { Pressable, View } from 'react-native'
import { Monitor, Smartphone, type LucideIcon } from 'lucide-react-native'
import type { Translate } from '../i18n/mobile-i18n'
import { useMobileTranslation } from '../i18n/use-mobile-translation'
import { colors, radii } from '../theme/mobile-theme'
import { themedStyleSheet } from '../theme/themed-styles'
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
import type { MobileBrowserViewMode } from './browser-screencast-request'
type Props = {
disabled: boolean
value: MobileBrowserViewMode
onChange: (mode: MobileBrowserViewMode) => void
}
function viewModes(t: Translate): { id: MobileBrowserViewMode; label: string; icon: LucideIcon }[] {
return [
{
id: 'web',
label: t('browser.viewModeSwitch.webView', 'Show web website view'),
icon: Monitor
},
{
id: 'mobile',
label: t('browser.viewModeSwitch.mobileView', 'Show mobile website view'),
icon: Smartphone
}
]
}
export function MobileBrowserViewModeSwitch({
disabled,
value,
onChange
}: Props): React.JSX.Element {
const t = useMobileTranslation()
useMobileColorScheme()
return (
<View style={styles.switch}>
{viewModes(t).map((mode) => (
<ViewModeButton
key={mode.id}
Icon={mode.icon}
label={mode.label}
selected={value === mode.id}
disabled={disabled}
onPress={() => onChange(mode.id)}
/>
))}
</View>
)
}
function ViewModeButton({
Icon,
disabled,
label,
onPress,
selected
}: {
Icon: LucideIcon
disabled?: boolean
label: string
onPress: () => void
selected: boolean
}) {
useMobileColorScheme()
return (
<Pressable
style={({ pressed }) => [
styles.button,
selected && styles.buttonSelected,
pressed && !disabled && !selected && styles.buttonPressed,
disabled && styles.disabled
]}
disabled={disabled}
onPress={onPress}
accessibilityRole="button"
accessibilityState={{ selected, disabled }}
accessibilityLabel={label}
>
<Icon size={14} color={selected ? colors.bgBase : colors.textSecondary} />
</Pressable>
)
}
const styles = themedStyleSheet(() => ({
switch: {
minHeight: 28,
flexDirection: 'row',
alignItems: 'center',
borderRadius: radii.control,
backgroundColor: colors.bgRaised,
padding: 2
},
button: {
minHeight: 24,
width: 32,
alignItems: 'center',
justifyContent: 'center',
borderRadius: radii.control
},
buttonPressed: {
backgroundColor: colors.borderSubtle
},
buttonSelected: {
backgroundColor: colors.textPrimary
},
disabled: {
opacity: 0.35
}
}))