Add shared trigger components and horizontal desktop workspace arguments
- 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.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
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 }],
|
||||
},
|
||||
}));
|
||||
@@ -303,7 +303,7 @@ interface TriggerState {
|
||||
}
|
||||
type TriggerStyleProp = StyleProp<ViewStyle> | ((state: TriggerState) => StyleProp<ViewStyle>);
|
||||
|
||||
interface DropdownMenuTriggerProps extends Omit<PressableProps, "style" | "children"> {
|
||||
export interface DropdownMenuTriggerProps extends Omit<PressableProps, "style" | "children"> {
|
||||
style?: TriggerStyleProp;
|
||||
children: ReactNode | ((state: TriggerState) => ReactNode);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
import { View } from "react-native";
|
||||
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
||||
import { ChevronDown } from "lucide-react-native";
|
||||
import { ICON_SIZE, type Theme } from "@/styles/theme";
|
||||
import { DropdownMenuTrigger, type DropdownMenuTriggerProps } from "@/components/ui/dropdown-menu";
|
||||
|
||||
const ThemedChevronDown = withUnistyles(ChevronDown);
|
||||
|
||||
interface DropdownTriggerProps extends Omit<DropdownMenuTriggerProps, "children"> {
|
||||
children?: ReactNode;
|
||||
chevron?: ReactNode | null;
|
||||
}
|
||||
|
||||
const chevronColorMapping = (theme: Theme) => ({
|
||||
color: theme.colors.foregroundMuted,
|
||||
});
|
||||
|
||||
export function DropdownTrigger({
|
||||
children,
|
||||
chevron,
|
||||
...props
|
||||
}: DropdownTriggerProps): ReactElement {
|
||||
return (
|
||||
<DropdownMenuTrigger {...props}>
|
||||
<View style={styles.row}>
|
||||
{children}
|
||||
{chevron !== null &&
|
||||
(chevron ?? (
|
||||
<View style={styles.chevronContainer}>
|
||||
<ThemedChevronDown size={ICON_SIZE.sm} uniProps={chevronColorMapping} />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</DropdownMenuTrigger>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create((theme) => ({
|
||||
row: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing[1],
|
||||
},
|
||||
chevronContainer: {
|
||||
transform: [{ translateY: 1 }],
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user