Files
claudemesh/packages/ui/mobile/src/components/dropdown-menu.tsx
Alejandro Gutiérrez d3163a5bff feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:19:32 +01:00

292 lines
8.0 KiB
TypeScript

import * as DropdownMenuPrimitive from "@rn-primitives/dropdown-menu";
import * as React from "react";
import { Platform, StyleSheet, Text, View } from "react-native";
import Animated from "react-native-reanimated";
import { FadeIn } from "react-native-reanimated";
import { FullWindowOverlay as RNFullWindowOverlay } from "react-native-screens";
import { cn } from "@turbostarter/ui";
import { Icons } from "./icons";
import { TextClassContext } from "./text";
import type { StyleProp, TextProps, ViewStyle } from "react-native";
const DropdownMenu = DropdownMenuPrimitive.Root;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
function DropdownMenuSubTrigger({
className,
inset,
children,
iconClassName,
...props
}: DropdownMenuPrimitive.SubTriggerProps &
React.RefAttributes<DropdownMenuPrimitive.SubTriggerRef> & {
children?: React.ReactNode;
iconClassName?: string;
inset?: boolean;
}) {
const { open } = DropdownMenuPrimitive.useSubContext();
const Icon = open ? Icons.ChevronUp : Icons.ChevronDown;
return (
<TextClassContext.Provider
value={cn(
"group-active:text-accent-foreground text-sm select-none",
open && "text-accent-foreground",
)}
>
<DropdownMenuPrimitive.SubTrigger
className={cn(
"active:bg-accent group flex flex-row items-center rounded-sm px-2 py-2 sm:py-1.5",
open && "bg-accent",
inset && "pl-8",
className,
)}
{...props}
>
<>{children}</>
<Icon
size={16}
className={cn(
"text-foreground ml-auto size-4 shrink-0",
iconClassName,
)}
/>
</DropdownMenuPrimitive.SubTrigger>
</TextClassContext.Provider>
);
}
function DropdownMenuSubContent({
className,
...props
}: DropdownMenuPrimitive.SubContentProps &
React.RefAttributes<DropdownMenuPrimitive.SubContentRef>) {
return (
<Animated.View entering={FadeIn}>
<DropdownMenuPrimitive.SubContent
className={cn(
"bg-popover border-border overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5",
className,
)}
{...props}
/>
</Animated.View>
);
}
const FullWindowOverlay =
Platform.OS === "ios" ? RNFullWindowOverlay : React.Fragment;
function DropdownMenuContent({
className,
overlayClassName,
overlayStyle,
portalHost,
...props
}: DropdownMenuPrimitive.ContentProps &
React.RefAttributes<DropdownMenuPrimitive.ContentRef> & {
overlayStyle?: StyleProp<ViewStyle>;
overlayClassName?: string;
portalHost?: string;
}) {
return (
<DropdownMenuPrimitive.Portal hostName={portalHost}>
<FullWindowOverlay>
<DropdownMenuPrimitive.Overlay
style={
overlayStyle
? StyleSheet.flatten([
StyleSheet.absoluteFill,
overlayStyle as typeof StyleSheet.absoluteFill,
])
: StyleSheet.absoluteFill
}
className={overlayClassName}
>
<Animated.View entering={FadeIn}>
<TextClassContext.Provider value="text-popover-foreground">
<DropdownMenuPrimitive.Content
className={cn(
"bg-popover border-border min-w-32 overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5",
className,
)}
{...props}
/>
</TextClassContext.Provider>
</Animated.View>
</DropdownMenuPrimitive.Overlay>
</FullWindowOverlay>
</DropdownMenuPrimitive.Portal>
);
}
function DropdownMenuItem({
className,
inset,
variant,
...props
}: DropdownMenuPrimitive.ItemProps &
React.RefAttributes<DropdownMenuPrimitive.ItemRef> & {
className?: string;
inset?: boolean;
variant?: "default" | "destructive";
}) {
return (
<TextClassContext.Provider
value={cn(
"text-popover-foreground group-active:text-popover-foreground text-sm select-none",
variant === "destructive" &&
"text-destructive group-active:text-destructive",
)}
>
<DropdownMenuPrimitive.Item
className={cn(
"active:bg-accent group relative flex flex-row items-center gap-2 rounded-sm px-2 py-2 sm:py-1.5",
variant === "destructive" &&
"active:bg-destructive/10 dark:active:bg-destructive/20",
props.disabled && "opacity-50",
inset && "pl-8",
className,
)}
{...props}
/>
</TextClassContext.Provider>
);
}
function DropdownMenuCheckboxItem({
className,
children,
...props
}: DropdownMenuPrimitive.CheckboxItemProps &
React.RefAttributes<DropdownMenuPrimitive.CheckboxItemRef> & {
children?: React.ReactNode;
}) {
return (
<TextClassContext.Provider value="text-sm text-popover-foreground select-none group-active:text-accent-foreground">
<DropdownMenuPrimitive.CheckboxItem
className={cn(
"active:bg-accent group relative flex flex-row items-center gap-2 rounded-sm py-2 pr-2 pl-8 sm:py-1.5",
props.disabled && "opacity-50",
className,
)}
{...props}
>
<View className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Icons.Check className={cn("text-foreground size-4")} />
</DropdownMenuPrimitive.ItemIndicator>
</View>
<>{children}</>
</DropdownMenuPrimitive.CheckboxItem>
</TextClassContext.Provider>
);
}
function DropdownMenuRadioItem({
className,
children,
...props
}: DropdownMenuPrimitive.RadioItemProps &
React.RefAttributes<DropdownMenuPrimitive.RadioItemRef> & {
children?: React.ReactNode;
}) {
return (
<TextClassContext.Provider value="text-sm text-popover-foreground select-none group-active:text-accent-foreground">
<DropdownMenuPrimitive.RadioItem
className={cn(
"active:bg-accent group relative flex flex-row items-center gap-2 rounded-sm py-2 pr-2 pl-8 sm:py-1.5",
props.disabled && "opacity-50",
className,
)}
{...props}
>
<View className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<View className="bg-foreground h-2 w-2 rounded-full" />
</DropdownMenuPrimitive.ItemIndicator>
</View>
<>{children}</>
</DropdownMenuPrimitive.RadioItem>
</TextClassContext.Provider>
);
}
function DropdownMenuLabel({
className,
inset,
...props
}: DropdownMenuPrimitive.LabelProps &
React.RefAttributes<DropdownMenuPrimitive.LabelRef> & {
className?: string;
inset?: boolean;
}) {
return (
<DropdownMenuPrimitive.Label
className={cn(
"text-foreground font-sans-medium px-2 py-2 text-xs sm:py-1.5",
inset && "pl-8",
className,
)}
{...props}
/>
);
}
function DropdownMenuSeparator({
className,
...props
}: DropdownMenuPrimitive.SeparatorProps &
React.RefAttributes<DropdownMenuPrimitive.SeparatorRef>) {
return (
<DropdownMenuPrimitive.Separator
className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props}
/>
);
}
function DropdownMenuShortcut({
className,
...props
}: TextProps & React.RefAttributes<Text>) {
return (
<Text
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className,
)}
{...props}
/>
);
}
export {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
};