// TransformControlPanel: absolute flip + 90-degree rotation, themed and // controlled. Every change emits the full TransformInput (the transform verb is // absolute). Toggles read the `active` theme slot for their on state. import type { StyleProp, ViewStyle } from 'react-native'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import type { TransformInput } from '../../kaleidoscope/types'; import { flipTestId, rotateTestId, TRANSFORM_TESTID_PREFIX } from '../../lib/test-id'; import { useThemeSlot } from '../theme/provider'; import { ControlSection } from './control-section'; const ROTATIONS = [0, 90, 180, 270] as const; export type TransformControlPanelProps = { readonly flip?: { readonly x?: boolean; readonly y?: boolean }; readonly rotate?: number; readonly onChange: (transform: TransformInput) => void; readonly disabled?: boolean; /** Root for this instance's test ids; override when a screen mounts two. */ readonly testIDPrefix?: string; }; function FlipToggle({ label, icon, on, disabled, onPress, testID, accessibilityLabel, }: { readonly label: string; readonly icon: string; readonly on: boolean; readonly disabled: boolean; readonly onPress: () => void; readonly testID: string; readonly accessibilityLabel: string; }) { const { style: activeStyle } = useThemeSlot('active'); return ( ), disabled && styles.disabled, ]} > {icon} {label} ); } export function TransformControlPanel({ flip, rotate = 0, onChange, disabled = false, testIDPrefix = TRANSFORM_TESTID_PREFIX, }: TransformControlPanelProps) { const x = flip?.x ?? false; const y = flip?.y ?? false; const { style: activeStyle } = useThemeSlot('active'); return ( onChange({ flip: { x: !x, y }, rotate })} testID={flipTestId(testIDPrefix, 'x')} accessibilityLabel="Flip horizontal" /> onChange({ flip: { x, y: !y }, rotate })} testID={flipTestId(testIDPrefix, 'y')} accessibilityLabel="Flip vertical" /> {ROTATIONS.map((deg) => { const on = rotate === deg; return ( onChange({ flip: { x, y }, rotate: deg })} style={[ styles.rot, on && styles.on, on && (activeStyle as StyleProp), disabled && styles.disabled, ]} > {deg}° ); })} ); } const styles = StyleSheet.create({ flipRow: { flexDirection: 'row', gap: 8 }, rotRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 }, flip: { flex: 1, paddingVertical: 12, backgroundColor: '#2a2a2a', borderRadius: 6, alignItems: 'center', gap: 2, }, rot: { flexGrow: 1, minWidth: 56, paddingVertical: 10, backgroundColor: '#2a2a2a', borderRadius: 6, alignItems: 'center', }, on: { backgroundColor: '#4a8f3f' }, disabled: { opacity: 0.5 }, flipIcon: { color: '#fff', fontSize: 22, lineHeight: 26 }, flipLabel: { color: '#fff', fontWeight: '500', fontSize: 13 }, rotText: { color: '#fff', fontWeight: '600', fontSize: 13 }, });