- Add DropdownTrigger and ComboboxTrigger with an overridable chevron slot and a 1 px translateY caret nudge for consistent vertical alignment. - Convert text-based dropdown/combobox triggers across model selector, agent controls, mode control, settings, and new workspace to use the shared components. - Lay out New Workspace arguments horizontally on desktop without labels while keeping the mobile vertical stack; align the project icon left edge with the title.
72 lines
1.9 KiB
TypeScript
72 lines
1.9 KiB
TypeScript
import { forwardRef, useCallback, type ReactElement, type ReactNode } from "react";
|
|
import {
|
|
Pressable,
|
|
View,
|
|
type PressableProps,
|
|
type PressableStateCallbackType,
|
|
type ViewStyle,
|
|
type StyleProp,
|
|
} from "react-native";
|
|
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
|
import { ChevronDown } from "lucide-react-native";
|
|
import { ICON_SIZE, type Theme } from "@/styles/theme";
|
|
|
|
const ThemedChevronDown = withUnistyles(ChevronDown);
|
|
|
|
const chevronColorMapping = (theme: Theme) => ({
|
|
color: theme.colors.foregroundMuted,
|
|
});
|
|
|
|
interface TriggerState {
|
|
pressed: boolean;
|
|
hovered: boolean;
|
|
}
|
|
|
|
type TriggerStyleProp = StyleProp<ViewStyle> | ((state: TriggerState) => StyleProp<ViewStyle>);
|
|
|
|
interface ComboboxTriggerProps extends Omit<PressableProps, "style" | "children"> {
|
|
style?: TriggerStyleProp;
|
|
children?: ReactNode;
|
|
chevron?: ReactNode | null;
|
|
}
|
|
|
|
export const ComboboxTrigger = forwardRef<View, ComboboxTriggerProps>(function ComboboxTrigger(
|
|
{ children, chevron, style, ...props },
|
|
ref,
|
|
): ReactElement {
|
|
const pressableStyle = useCallback(
|
|
({ pressed, hovered = false }: PressableStateCallbackType & { hovered?: boolean }) => {
|
|
if (typeof style === "function") {
|
|
return style({ pressed, hovered });
|
|
}
|
|
return style;
|
|
},
|
|
[style],
|
|
);
|
|
|
|
return (
|
|
<Pressable ref={ref} collapsable={false} style={pressableStyle} {...props}>
|
|
<View style={styles.row}>
|
|
{children}
|
|
{chevron !== null &&
|
|
(chevron ?? (
|
|
<View style={styles.chevronContainer}>
|
|
<ThemedChevronDown size={ICON_SIZE.sm} uniProps={chevronColorMapping} />
|
|
</View>
|
|
))}
|
|
</View>
|
|
</Pressable>
|
|
);
|
|
});
|
|
|
|
const styles = StyleSheet.create((theme) => ({
|
|
row: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
gap: theme.spacing[1],
|
|
},
|
|
chevronContainer: {
|
|
transform: [{ translateY: 1 }],
|
|
},
|
|
}));
|