diff --git a/apps/native/src/components/DrawerToggle.tsx b/apps/native/src/components/DrawerToggle.tsx index 3385ab5..743186c 100644 --- a/apps/native/src/components/DrawerToggle.tsx +++ b/apps/native/src/components/DrawerToggle.tsx @@ -5,10 +5,13 @@ import { AlignJustify } from "~/components/Icons"; import { cn } from "~/lib/utils"; export function DrawerToggle() { - const navigation = useNavigation>(); + const navigation = + useNavigation< + DrawerNavigationProp<{ + Home: undefined; + Settings: undefined; + }> + >(); return ( (null); const Root = React.forwardRef( - ( - { - asChild, - type, - disabled, - collapsible = true, - value: valueProp, - onValueChange: onValueChangeProps, - defaultValue, - ...viewProps - }, - ref - ) => { - const [value = type === 'multiple' ? [] : undefined, onValueChange] = useControllableState< - (string | undefined) | string[] - >({ - prop: valueProp, - defaultProp: defaultValue, - onChange: onValueChangeProps as (state: string | string[] | undefined) => void, - }); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ( + { + asChild, + type, + disabled, + collapsible = true, + value: valueProp, + onValueChange: onValueChangeProps, + defaultValue, + ...viewProps + }, + ref, + ) => { + const [value = type === "multiple" ? [] : undefined, onValueChange] = + useControllableState<(string | undefined) | string[]>({ + prop: valueProp, + defaultProp: defaultValue, + onChange: onValueChangeProps as ( + state: string | string[] | undefined, + ) => void, + }); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeAccordion'; +Root.displayName = "RootNativeAccordion"; function useRootContext() { - const context = React.useContext(AccordionContext); - if (!context) { - throw new Error( - 'Accordion compound components cannot be rendered outside the Accordion component' - ); - } - return context; + const context = React.useContext(AccordionContext); + if (!context) { + throw new Error( + "Accordion compound components cannot be rendered outside the Accordion component", + ); + } + return context; } type AccordionItemContext = AccordionItemProps & { - nativeID: string; - isExpanded: boolean; + nativeID: string; + isExpanded: boolean; }; -const AccordionItemContext = React.createContext(null); +const AccordionItemContext = React.createContext( + null, +); const Item = React.forwardRef( - ({ asChild, value, disabled, ...viewProps }, ref) => { - const { value: rootValue } = useRootContext(); - const nativeID = React.useId(); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ({ asChild, value, disabled, ...viewProps }, ref) => { + const { value: rootValue } = useRootContext(); + const nativeID = React.useId(); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Item.displayName = 'ItemNativeAccordion'; +Item.displayName = "ItemNativeAccordion"; function useItemContext() { - const context = React.useContext(AccordionItemContext); - if (!context) { - throw new Error( - 'AccordionItem compound components cannot be rendered outside the AccordionItem component' - ); - } - return context; + const context = React.useContext(AccordionItemContext); + if (!context) { + throw new Error( + "AccordionItem compound components cannot be rendered outside the AccordionItem component", + ); + } + return context; } -const Header = React.forwardRef(({ asChild, ...props }, ref) => { - const { disabled: rootDisabled } = useRootContext(); - const { disabled: itemDisabled, isExpanded } = useItemContext(); - - const Component = asChild ? Slot.View : View; - return ( - - ); -}); +const Header = React.forwardRef( + ({ asChild, ...props }, ref) => { + const { disabled: rootDisabled } = useRootContext(); + const { disabled: itemDisabled, isExpanded } = useItemContext(); + + const Component = asChild ? Slot.View : View; + return ( + + ); + }, +); -Header.displayName = 'HeaderNativeAccordion'; +Header.displayName = "HeaderNativeAccordion"; const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled: disabledProp, ...props }, ref) => { - const { - disabled: rootDisabled, - type, - onValueChange, - value: rootValue, - collapsible, - } = useRootContext(); - const { nativeID, disabled: itemDisabled, value, isExpanded } = useItemContext(); - - function onPress(ev: GestureResponderEvent) { - if (rootDisabled || itemDisabled) return; - if (type === 'single') { - const newValue = collapsible ? (value === rootValue ? undefined : value) : value; - onValueChange(newValue); - } - if (type === 'multiple') { - const rootToArray = toStringArray(rootValue); - const newValue = collapsible - ? rootToArray.includes(value) - ? rootToArray.filter((val) => val !== value) - : rootToArray.concat(value) - : [...new Set(rootToArray.concat(value))]; - // @ts-ignore - `newValue` is of type `string[]` which is OK - onValueChange(newValue); - } - onPressProp?.(ev); - } - - const isDisabled = disabledProp || rootDisabled || itemDisabled; - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ( + { asChild, onPress: onPressProp, disabled: disabledProp, ...props }, + ref, + ) => { + const { + disabled: rootDisabled, + type, + onValueChange, + value: rootValue, + collapsible, + } = useRootContext(); + const { + nativeID, + disabled: itemDisabled, + value, + isExpanded, + } = useItemContext(); + + function onPress(ev: GestureResponderEvent) { + if (rootDisabled || itemDisabled) return; + if (type === "single") { + const newValue = collapsible + ? value === rootValue + ? undefined + : value + : value; + onValueChange(newValue as string & string[]); + } + if (type === "multiple") { + const rootToArray = toStringArray(rootValue); + const newValue = collapsible + ? rootToArray.includes(value) + ? rootToArray.filter((val) => val !== value) + : rootToArray.concat(value) + : [...new Set(rootToArray.concat(value))]; + // @ts-ignore - `newValue` is of type `string[]` which is OK + onValueChange(newValue); + } + onPressProp?.(ev); + } + + const isDisabled = disabledProp || rootDisabled || itemDisabled; + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeAccordion'; - -const Content = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { type } = useRootContext(); - const { nativeID, isExpanded } = useItemContext(); - - if (!forceMount) { - if (!isExpanded) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } -); +Trigger.displayName = "TriggerNativeAccordion"; + +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & AccordionContentProps +>(({ asChild, forceMount, ...props }, ref) => { + const { type } = useRootContext(); + const { nativeID, isExpanded } = useItemContext(); + + if (!forceMount) { + if (!isExpanded) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -Content.displayName = 'ContentNativeAccordion'; +Content.displayName = "ContentNativeAccordion"; export { Content, Header, Item, Root, Trigger, useRootContext, useItemContext }; function toStringArray(value?: string | string[]) { - return Array.isArray(value) ? value : value ? [value] : []; + return Array.isArray(value) ? value : value ? [value] : []; } -function isItemExpanded(rootValue: string | string[] | undefined, value: string) { - return Array.isArray(rootValue) ? rootValue.includes(value) : rootValue === value; +function isItemExpanded( + rootValue: string | string[] | undefined, + value: string, +) { + return Array.isArray(rootValue) + ? rootValue.includes(value) + : rootValue === value; } diff --git a/apps/native/src/components/primitives/accordion/types.ts b/apps/native/src/components/primitives/accordion/types.ts index 1fc7e7f..b9a46c6 100644 --- a/apps/native/src/components/primitives/accordion/types.ts +++ b/apps/native/src/components/primitives/accordion/types.ts @@ -1,52 +1,52 @@ import type { ForceMountable } from "~/components/primitives/types"; type RootContext = { - type: "single" | "multiple"; - value: (string | undefined) | string[]; - onValueChange: - | ((value: string | undefined) => void) - | ((value: string[]) => void); - collapsible: boolean; - disabled?: boolean; + type: "single" | "multiple"; + value: (string | undefined) | string[]; + onValueChange: + | ((value: string | undefined) => void) + | ((value: string[]) => void); + collapsible: boolean; + disabled?: boolean; }; type SingleRootProps = { - type: "single"; - defaultValue?: string | undefined; - value?: string | undefined; - onValueChange?: (value: string | undefined) => void; + type: "single"; + defaultValue?: string | undefined; + value?: string | undefined; + onValueChange?: (value: string | undefined) => void; }; type MultipleRootProps = { - type: "multiple"; - defaultValue?: string[]; - value?: string[]; - onValueChange?: (value: string[]) => void; + type: "multiple"; + defaultValue?: string[]; + value?: string[]; + onValueChange?: (value: string[]) => void; }; type AccordionRootProps = (SingleRootProps | MultipleRootProps) & { - defaultValue?: string | string[]; - disabled?: boolean; - collapsible?: boolean; - /** - * Platform: WEB ONLY - */ - dir?: "ltr" | "rtl"; - /** - * Platform: WEB ONLY - */ - orientation?: "vertical" | "horizontal"; + defaultValue?: string | string[]; + disabled?: boolean; + collapsible?: boolean; + /** + * Platform: WEB ONLY + */ + dir?: "ltr" | "rtl"; + /** + * Platform: WEB ONLY + */ + orientation?: "vertical" | "horizontal"; }; type AccordionItemProps = { - value: string; - disabled?: boolean; + value: string; + disabled?: boolean; }; type AccordionContentProps = ForceMountable; export type { - AccordionContentProps, - AccordionItemProps, - AccordionRootProps, - RootContext, + AccordionContentProps, + AccordionItemProps, + AccordionRootProps, + RootContext, }; diff --git a/apps/native/src/components/primitives/alert-dialog/index.tsx b/apps/native/src/components/primitives/alert-dialog/index.tsx index 8dbae4f..fe3e9f1 100644 --- a/apps/native/src/components/primitives/alert-dialog/index.tsx +++ b/apps/native/src/components/primitives/alert-dialog/index.tsx @@ -1,237 +1,275 @@ -import { useControllableState } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; +import { useControllableState } from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - PressableRef, - SlottablePressableProps, - SlottableTextProps, - SlottableViewProps, - TextRef, - ViewRef, -} from '~/components/primitives/types'; -import * as React from 'react'; -import { BackHandler, Pressable, Text, View, type GestureResponderEvent } from 'react-native'; + PressableRef, + SlottablePressableProps, + SlottableTextProps, + SlottableViewProps, + TextRef, + ViewRef, +} from "~/components/primitives/types"; +import * as React from "react"; +import { + BackHandler, + Pressable, + Text, + View, + type GestureResponderEvent, +} from "react-native"; import type { - AlertDialogContentProps, - AlertDialogOverlayProps, - AlertDialogPortalProps, - AlertDialogRootProps, - RootContext, -} from './types'; - -const AlertDialogContext = React.createContext<(RootContext & { nativeID: string }) | null>(null); - -const Root = React.forwardRef( - ({ asChild, open: openProp, defaultOpen, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => { - const nativeID = React.useId(); - const [open = false, onOpenChange] = useControllableState({ - prop: openProp, - defaultProp: defaultOpen, - onChange: onOpenChangeProp, - }); - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + AlertDialogContentProps, + AlertDialogOverlayProps, + AlertDialogPortalProps, + AlertDialogRootProps, + RootContext, +} from "./types"; + +const AlertDialogContext = React.createContext< + (RootContext & { nativeID: string }) | null +>(null); + +const Root = React.forwardRef< + ViewRef, + SlottableViewProps & AlertDialogRootProps +>( + ( + { + asChild, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + ...viewProps + }, + ref, + ) => { + const nativeID = React.useId(); + const [open = false, onOpenChange] = useControllableState({ + prop: openProp, + defaultProp: defaultOpen, + onChange: onOpenChangeProp, + }); + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeAlertDialog'; +Root.displayName = "RootNativeAlertDialog"; function useRootContext() { - const context = React.useContext(AlertDialogContext); - if (!context) { - throw new Error( - 'AlertDialog compound components cannot be rendered outside the AlertDialog component' - ); - } - return context; + const context = React.useContext(AlertDialogContext); + if (!context) { + throw new Error( + "AlertDialog compound components cannot be rendered outside the AlertDialog component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { open: value, onOpenChange } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - onOpenChange(!value); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const { open: value, onOpenChange } = useRootContext(); + + function onPress(ev: GestureResponderEvent) { + onOpenChange(!value); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeAlertDialog'; +Trigger.displayName = "TriggerNativeAlertDialog"; /** * @warning when using a custom ``, you might have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: AlertDialogPortalProps) { - const value = useRootContext(); - - if (!forceMount) { - if (!value.open) { - return null; - } - } - - return ( - - {children} - - ); + const value = useRootContext(); + + if (!forceMount) { + if (!value.open) { + return null; + } + } + + return ( + + + {children} + + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { open: value } = useRootContext(); +const Overlay = React.forwardRef< + ViewRef, + SlottableViewProps & AlertDialogOverlayProps +>(({ asChild, forceMount, ...props }, ref) => { + const { open: value } = useRootContext(); - if (!forceMount) { - if (!value) { - return null; - } - } + if (!forceMount) { + if (!value) { + return null; + } + } - const Component = asChild ? Slot.View : View; - return ; - } -); + const Component = asChild ? Slot.View : View; + return ; +}); -Overlay.displayName = 'OverlayNativeAlertDialog'; - -const Content = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { open: value, nativeID, onOpenChange } = useRootContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - onOpenChange(false); - return true; - }); - - return () => { - backHandler.remove(); - }; - }, []); - - if (!forceMount) { - if (!value) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } -); +Overlay.displayName = "OverlayNativeAlertDialog"; + +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & AlertDialogContentProps +>(({ asChild, forceMount, ...props }, ref) => { + const { open: value, nativeID, onOpenChange } = useRootContext(); + + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + onOpenChange(false); + return true; + }, + ); + + return () => { + backHandler.remove(); + }; + }, []); + + if (!forceMount) { + if (!value) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -Content.displayName = 'ContentNativeAlertDialog'; +Content.displayName = "ContentNativeAlertDialog"; const Cancel = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { onOpenChange } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - onOpenChange(false); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const { onOpenChange } = useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + onOpenChange(false); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Cancel.displayName = 'CloseNativeAlertDialog'; +Cancel.displayName = "CloseNativeAlertDialog"; const Action = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { onOpenChange } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - onOpenChange(false); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const { onOpenChange } = useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + onOpenChange(false); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Action.displayName = 'ActionNativeAlertDialog'; - -const Title = React.forwardRef(({ asChild, ...props }, ref) => { - const { nativeID } = useRootContext(); - const Component = asChild ? Slot.Text : Text; - return ; -}); +Action.displayName = "ActionNativeAlertDialog"; + +const Title = React.forwardRef( + ({ asChild, ...props }, ref) => { + const { nativeID } = useRootContext(); + const Component = asChild ? Slot.Text : Text; + return ( + + ); + }, +); -Title.displayName = 'TitleNativeAlertDialog'; +Title.displayName = "TitleNativeAlertDialog"; -const Description = React.forwardRef(({ asChild, ...props }, ref) => { - const { nativeID } = useRootContext(); - const Component = asChild ? Slot.Text : Text; - return ; -}); +const Description = React.forwardRef( + ({ asChild, ...props }, ref) => { + const { nativeID } = useRootContext(); + const Component = asChild ? Slot.Text : Text; + return ; + }, +); -Description.displayName = 'DescriptionNativeAlertDialog'; +Description.displayName = "DescriptionNativeAlertDialog"; export { - Action, - Cancel, - Content, - Description, - Overlay, - Portal, - Root, - Title, - Trigger, - useRootContext, + Action, + Cancel, + Content, + Description, + Overlay, + Portal, + Root, + Title, + Trigger, + useRootContext, }; diff --git a/apps/native/src/components/primitives/alert-dialog/types.ts b/apps/native/src/components/primitives/alert-dialog/types.ts index b60fcc1..9dd3b7d 100644 --- a/apps/native/src/components/primitives/alert-dialog/types.ts +++ b/apps/native/src/components/primitives/alert-dialog/types.ts @@ -1,48 +1,48 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; type AlertDialogRootProps = { - open?: boolean; - onOpenChange?: (value: boolean) => void; - defaultOpen?: boolean; + open?: boolean; + onOpenChange?: (value: boolean) => void; + defaultOpen?: boolean; }; interface RootContext { - open: boolean; - onOpenChange: (value: boolean) => void; + open: boolean; + onOpenChange: (value: boolean) => void; } interface AlertDialogPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } type AlertDialogOverlayProps = ForceMountable; type AlertDialogContentProps = ForceMountable & { - /** - * Platform: WEB ONLY - */ - onOpenAutoFocus?: (ev: Event) => void; - /** - * Platform: WEB ONLY - */ - onCloseAutoFocus?: (ev: Event) => void; - /** - * Platform: WEB ONLY - */ - onEscapeKeyDown?: (ev: Event) => void; + /** + * Platform: WEB ONLY + */ + onOpenAutoFocus?: (ev: Event) => void; + /** + * Platform: WEB ONLY + */ + onCloseAutoFocus?: (ev: Event) => void; + /** + * Platform: WEB ONLY + */ + onEscapeKeyDown?: (ev: Event) => void; }; export type { - AlertDialogRootProps, - AlertDialogPortalProps, - AlertDialogOverlayProps, - AlertDialogContentProps, - RootContext, + AlertDialogRootProps, + AlertDialogPortalProps, + AlertDialogOverlayProps, + AlertDialogContentProps, + RootContext, }; diff --git a/apps/native/src/components/primitives/avatar/index.tsx b/apps/native/src/components/primitives/avatar/index.tsx index 41a3892..42ede29 100644 --- a/apps/native/src/components/primitives/avatar/index.tsx +++ b/apps/native/src/components/primitives/avatar/index.tsx @@ -1,95 +1,117 @@ -import * as React from 'react'; +import * as React from "react"; import { - ImageErrorEventData, - ImageLoadEventData, - NativeSyntheticEvent, - Image as RNImage, - View, -} from 'react-native'; -import * as Slot from '~/components/primitives/slot'; -import { ComponentPropsWithAsChild, SlottableViewProps, ViewRef } from '~/components/primitives/types'; -import { AvatarImageProps, AvatarRootProps } from './types'; - -type AvatarState = 'loading' | 'error' | 'loaded'; + ImageErrorEventData, + ImageLoadEventData, + NativeSyntheticEvent, + Image as RNImage, + View, +} from "react-native"; +import * as Slot from "~/components/primitives/slot"; +import { + ComponentPropsWithAsChild, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; +import { AvatarImageProps, AvatarRootProps } from "./types"; + +type AvatarState = "loading" | "error" | "loaded"; interface IRootContext extends AvatarRootProps { - status: AvatarState; - setStatus: (status: AvatarState) => void; + status: AvatarState; + setStatus: (status: AvatarState) => void; } const RootContext = React.createContext(null); const Root = React.forwardRef( - ({ asChild, alt, ...viewProps }, ref) => { - const [status, setStatus] = React.useState('loading'); - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ({ asChild, alt, ...viewProps }, ref) => { + const [status, setStatus] = React.useState("loading"); + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootAvatar'; +Root.displayName = "RootAvatar"; function useRootContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error('Avatar compound components cannot be rendered outside the Avatar component'); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "Avatar compound components cannot be rendered outside the Avatar component", + ); + } + return context; } const Image = React.forwardRef< - React.ElementRef, - Omit, 'alt'> & AvatarImageProps + React.ElementRef, + Omit, "alt"> & AvatarImageProps >( - ( - { asChild, onLoad: onLoadProps, onError: onErrorProps, onLoadingStatusChange, ...props }, - ref - ) => { - const { alt, setStatus, status } = useRootContext(); - - const onLoad = React.useCallback( - (e: NativeSyntheticEvent) => { - setStatus('loaded'); - onLoadingStatusChange?.('loaded'); - onLoadProps?.(e); - }, - [onLoadProps] - ); - - const onError = React.useCallback( - (e: NativeSyntheticEvent) => { - setStatus('error'); - onLoadingStatusChange?.('error'); - onErrorProps?.(e); - }, - [onErrorProps] - ); - - if (status === 'error') { - return null; - } - - const Component = asChild ? Slot.Image : RNImage; - return ; - } + ( + { + asChild, + onLoad: onLoadProps, + onError: onErrorProps, + onLoadingStatusChange, + ...props + }, + ref, + ) => { + const { alt, setStatus, status } = useRootContext(); + + const onLoad = React.useCallback( + (e: NativeSyntheticEvent) => { + setStatus("loaded"); + onLoadingStatusChange?.("loaded"); + onLoadProps?.(e); + }, + [onLoadProps], + ); + + const onError = React.useCallback( + (e: NativeSyntheticEvent) => { + setStatus("error"); + onLoadingStatusChange?.("error"); + onErrorProps?.(e); + }, + [onErrorProps], + ); + + if (status === "error") { + return null; + } + + const Component = asChild ? Slot.Image : RNImage; + return ( + + ); + }, ); -Image.displayName = 'ImageAvatar'; +Image.displayName = "ImageAvatar"; -const Fallback = React.forwardRef(({ asChild, ...props }, ref) => { - const { alt, status } = useRootContext(); +const Fallback = React.forwardRef( + ({ asChild, ...props }, ref) => { + const { alt, status } = useRootContext(); - if (status !== 'error') { - return null; - } - const Component = asChild ? Slot.View : View; - return ; -}); + if (status !== "error") { + return null; + } + const Component = asChild ? Slot.View : View; + return ; + }, +); -Fallback.displayName = 'FallbackAvatar'; +Fallback.displayName = "FallbackAvatar"; export { Fallback, Image, Root }; diff --git a/apps/native/src/components/primitives/avatar/types.ts b/apps/native/src/components/primitives/avatar/types.ts index 4d6d9c0..d9c3cbf 100644 --- a/apps/native/src/components/primitives/avatar/types.ts +++ b/apps/native/src/components/primitives/avatar/types.ts @@ -1,10 +1,10 @@ interface AvatarRootProps { - alt: string; + alt: string; } interface AvatarImageProps { - children?: React.ReactNode; - onLoadingStatusChange?: (status: 'error' | 'loaded') => void; + children?: React.ReactNode; + onLoadingStatusChange?: (status: "error" | "loaded") => void; } export type { AvatarRootProps, AvatarImageProps }; diff --git a/apps/native/src/components/primitives/checkbox/index.tsx b/apps/native/src/components/primitives/checkbox/index.tsx index 8240558..e6df25a 100644 --- a/apps/native/src/components/primitives/checkbox/index.tsx +++ b/apps/native/src/components/primitives/checkbox/index.tsx @@ -1,101 +1,112 @@ -import * as React from 'react'; -import { GestureResponderEvent, Pressable, View } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; -import type { ComponentPropsWithAsChild, PressableRef, SlottablePressableProps } from '~/components/primitives/types'; -import type { CheckboxIndicator, CheckboxRootProps } from './types'; +import * as React from "react"; +import { GestureResponderEvent, Pressable, View } from "react-native"; +import * as Slot from "~/components/primitives/slot"; +import type { + ComponentPropsWithAsChild, + PressableRef, + SlottablePressableProps, +} from "~/components/primitives/types"; +import type { CheckboxIndicator, CheckboxRootProps } from "./types"; interface RootContext extends CheckboxRootProps { - nativeID?: string; + nativeID?: string; } const CheckboxContext = React.createContext(null); -const Root = React.forwardRef( - ({ asChild, disabled = false, checked, onCheckedChange, nativeID, ...props }, ref) => { - return ( - - - - ); - } +const Root = React.forwardRef< + PressableRef, + SlottablePressableProps & CheckboxRootProps +>( + ( + { asChild, disabled = false, checked, onCheckedChange, nativeID, ...props }, + ref, + ) => { + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeCheckbox'; +Root.displayName = "RootNativeCheckbox"; function useCheckboxContext() { - const context = React.useContext(CheckboxContext); - if (!context) { - throw new Error( - 'Checkbox compound components cannot be rendered outside the Checkbox component' - ); - } - return context; + const context = React.useContext(CheckboxContext); + if (!context) { + throw new Error( + "Checkbox compound components cannot be rendered outside the Checkbox component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, ...props }, ref) => { - const { disabled, checked, onCheckedChange, nativeID } = useCheckboxContext(); + ({ asChild, onPress: onPressProp, ...props }, ref) => { + const { disabled, checked, onCheckedChange, nativeID } = + useCheckboxContext(); - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - const newValue = !checked; - onCheckedChange(newValue); - onPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + const newValue = !checked; + onCheckedChange(newValue); + onPressProp?.(ev); + } - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeCheckbox'; +Trigger.displayName = "TriggerNativeCheckbox"; const Indicator = React.forwardRef< - React.ElementRef, - ComponentPropsWithAsChild & CheckboxIndicator + React.ElementRef, + ComponentPropsWithAsChild & CheckboxIndicator >(({ asChild, forceMount, ...props }, ref) => { - const { checked, disabled } = useCheckboxContext(); + const { checked, disabled } = useCheckboxContext(); - if (!forceMount) { - if (!checked) { - return null; - } - } + if (!forceMount) { + if (!checked) { + return null; + } + } - const Component = asChild ? Slot.View : View; - return ( - - ); + const Component = asChild ? Slot.View : View; + return ( + + ); }); -Indicator.displayName = 'IndicatorNativeCheckbox'; +Indicator.displayName = "IndicatorNativeCheckbox"; export { Indicator, Root }; diff --git a/apps/native/src/components/primitives/checkbox/types.ts b/apps/native/src/components/primitives/checkbox/types.ts index c3bf2bc..705d5c8 100644 --- a/apps/native/src/components/primitives/checkbox/types.ts +++ b/apps/native/src/components/primitives/checkbox/types.ts @@ -1,9 +1,9 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface CheckboxRootProps { - checked: boolean; - onCheckedChange: (checked: boolean) => void; - disabled?: boolean; + checked: boolean; + onCheckedChange: (checked: boolean) => void; + disabled?: boolean; } type CheckboxIndicator = ForceMountable; diff --git a/apps/native/src/components/primitives/collapsible/index.tsx b/apps/native/src/components/primitives/collapsible/index.tsx index 6243120..9420169 100644 --- a/apps/native/src/components/primitives/collapsible/index.tsx +++ b/apps/native/src/components/primitives/collapsible/index.tsx @@ -1,119 +1,132 @@ -import { useControllableState } from '~/components/primitives/hooks'; -import * as Slot from '~/components/primitives/slot'; +import { useControllableState } from "~/components/primitives/hooks"; +import * as Slot from "~/components/primitives/slot"; import type { - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; -import * as React from 'react'; -import { Pressable, View, type GestureResponderEvent } from 'react-native'; -import type { CollapsibleContentProps, CollapsibleRootProps, RootContext } from './types'; + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; +import * as React from "react"; +import { Pressable, View, type GestureResponderEvent } from "react-native"; +import type { + CollapsibleContentProps, + CollapsibleRootProps, + RootContext, +} from "./types"; -const CollapsibleContext = React.createContext<(RootContext & { nativeID: string }) | null>(null); +const CollapsibleContext = React.createContext< + (RootContext & { nativeID: string }) | null +>(null); -const Root = React.forwardRef( - ( - { - asChild, - disabled = false, - open: openProp, - defaultOpen, - onOpenChange: onOpenChangeProp, - ...viewProps - }, - ref - ) => { - const nativeID = React.useId(); - const [open = false, onOpenChange] = useControllableState({ - prop: openProp, - defaultProp: defaultOpen, - onChange: onOpenChangeProp, - }); +const Root = React.forwardRef< + ViewRef, + SlottableViewProps & CollapsibleRootProps +>( + ( + { + asChild, + disabled = false, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + ...viewProps + }, + ref, + ) => { + const nativeID = React.useId(); + const [open = false, onOpenChange] = useControllableState({ + prop: openProp, + defaultProp: defaultOpen, + onChange: onOpenChangeProp, + }); - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeCollapsible'; +Root.displayName = "RootNativeCollapsible"; function useCollapsibleContext() { - const context = React.useContext(CollapsibleContext); - if (!context) { - throw new Error( - 'Collapsible compound components cannot be rendered outside the Collapsible component' - ); - } - return context; + const context = React.useContext(CollapsibleContext); + if (!context) { + throw new Error( + "Collapsible compound components cannot be rendered outside the Collapsible component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled: disabledProp = false, ...props }, ref) => { - const { disabled, open, onOpenChange, nativeID } = useCollapsibleContext(); + ( + { asChild, onPress: onPressProp, disabled: disabledProp = false, ...props }, + ref, + ) => { + const { disabled, open, onOpenChange, nativeID } = useCollapsibleContext(); - function onPress(ev: GestureResponderEvent) { - if (disabled || disabledProp) return; - onOpenChange(!open); - onPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + if (disabled || disabledProp) return; + onOpenChange(!open); + onPressProp?.(ev); + } - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeCollapsible'; +Trigger.displayName = "TriggerNativeCollapsible"; -const Content = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { nativeID, open } = useCollapsibleContext(); +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & CollapsibleContentProps +>(({ asChild, forceMount, ...props }, ref) => { + const { nativeID, open } = useCollapsibleContext(); - if (!forceMount) { - if (!open) { - return null; - } - } + if (!forceMount) { + if (!open) { + return null; + } + } - const Component = asChild ? Slot.View : View; - return ( - - ); - } -); + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -Content.displayName = 'ContentNativeCollapsible'; +Content.displayName = "ContentNativeCollapsible"; export { Content, Root, Trigger }; diff --git a/apps/native/src/components/primitives/collapsible/types.ts b/apps/native/src/components/primitives/collapsible/types.ts index 2d2d7d5..d560dd8 100644 --- a/apps/native/src/components/primitives/collapsible/types.ts +++ b/apps/native/src/components/primitives/collapsible/types.ts @@ -1,16 +1,16 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface RootContext { - open: boolean; - onOpenChange: (open: boolean) => void; - disabled: boolean; + open: boolean; + onOpenChange: (open: boolean) => void; + disabled: boolean; } interface CollapsibleRootProps { - open?: boolean; - defaultOpen?: boolean; - onOpenChange?: (open: boolean) => void; - disabled?: boolean; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (open: boolean) => void; + disabled?: boolean; } type CollapsibleContentProps = ForceMountable; diff --git a/apps/native/src/components/primitives/context-menu/index.tsx b/apps/native/src/components/primitives/context-menu/index.tsx index 756c01f..cc9efe3 100644 --- a/apps/native/src/components/primitives/context-menu/index.tsx +++ b/apps/native/src/components/primitives/context-menu/index.tsx @@ -1,626 +1,694 @@ -import * as React from 'react'; +import * as React from "react"; import { - BackHandler, - Pressable, - Text, - View, - type AccessibilityActionEvent, - type GestureResponderEvent, - type LayoutChangeEvent, - type LayoutRectangle, -} from 'react-native'; -import { useRelativePosition, type LayoutPosition } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; + BackHandler, + Pressable, + Text, + View, + type AccessibilityActionEvent, + type GestureResponderEvent, + type LayoutChangeEvent, + type LayoutRectangle, +} from "react-native"; +import { + useRelativePosition, + type LayoutPosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - ForceMountable, - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableTextProps, - SlottableViewProps, - TextRef, - ViewRef, -} from '~/components/primitives/types'; + ForceMountable, + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableTextProps, + SlottableViewProps, + TextRef, + ViewRef, +} from "~/components/primitives/types"; import type { - ContextMenuCheckboxItemProps, - ContextMenuItemProps, - ContextMenuOverlayProps, - ContextMenuPortalProps, - ContextMenuRadioGroupProps, - ContextMenuRadioItemProps, - ContextMenuRootProps, - ContextMenuSeparatorProps, - ContextMenuSubProps, - ContextMenuSubTriggerProps, -} from './types'; + ContextMenuCheckboxItemProps, + ContextMenuItemProps, + ContextMenuOverlayProps, + ContextMenuPortalProps, + ContextMenuRadioGroupProps, + ContextMenuRadioItemProps, + ContextMenuRootProps, + ContextMenuSeparatorProps, + ContextMenuSubProps, + ContextMenuSubTriggerProps, +} from "./types"; interface IRootContext extends ContextMenuRootProps { - pressPosition: LayoutPosition | null; - setPressPosition: (pressPosition: LayoutPosition | null) => void; - contentLayout: LayoutRectangle | null; - setContentLayout: (contentLayout: LayoutRectangle | null) => void; - nativeID: string; + pressPosition: LayoutPosition | null; + setPressPosition: (pressPosition: LayoutPosition | null) => void; + contentLayout: LayoutRectangle | null; + setContentLayout: (contentLayout: LayoutRectangle | null) => void; + nativeID: string; } const RootContext = React.createContext(null); -const Root = React.forwardRef( - ({ asChild, open, onOpenChange, relativeTo = 'longPress', ...viewProps }, ref) => { - const nativeID = React.useId(); - const [pressPosition, setPressPosition] = React.useState(null); - const [contentLayout, setContentLayout] = React.useState(null); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } +const Root = React.forwardRef< + ViewRef, + SlottableViewProps & ContextMenuRootProps +>( + ( + { asChild, open, onOpenChange, relativeTo = "longPress", ...viewProps }, + ref, + ) => { + const nativeID = React.useId(); + const [pressPosition, setPressPosition] = + React.useState(null); + const [contentLayout, setContentLayout] = + React.useState(null); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeContextMenu'; +Root.displayName = "RootNativeContextMenu"; function useRootContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error( - 'ContextMenu compound components cannot be rendered outside the ContextMenu component' - ); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "ContextMenu compound components cannot be rendered outside the ContextMenu component", + ); + } + return context; } -const accessibilityActions = [{ name: 'longpress' }]; +const accessibilityActions = [{ name: "longpress" }]; const Trigger = React.forwardRef( - ( - { - asChild, - onLongPress: onLongPressProp, - disabled = false, - onAccessibilityAction: onAccessibilityActionProp, - ...props - }, - ref - ) => { - const triggerRef = React.useRef(null); - const { open, onOpenChange, relativeTo, setPressPosition } = useRootContext(); - - React.useImperativeHandle( - ref, - () => { - if (!triggerRef.current) { - return new View({}); - } - return triggerRef.current; - }, - [triggerRef.current] - ); - - function onLongPress(ev: GestureResponderEvent) { - if (disabled) return; - if (relativeTo === 'longPress') { - setPressPosition({ - width: 0, - pageX: ev.nativeEvent.pageX, - pageY: ev.nativeEvent.pageY, - height: 0, - }); - } - if (relativeTo === 'trigger') { - triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { - setPressPosition({ width, pageX, pageY: pageY, height }); - }); - } - onOpenChange(!open); - onLongPressProp?.(ev); - } - - function onAccessibilityAction(event: AccessibilityActionEvent) { - if (disabled) return; - if (event.nativeEvent.actionName === 'longpress') { - setPressPosition({ - width: 0, - pageX: 0, - pageY: 0, - height: 0, - }); - const newValue = !open; - onOpenChange(newValue); - } - onAccessibilityActionProp?.(event); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ( + { + asChild, + onLongPress: onLongPressProp, + disabled = false, + onAccessibilityAction: onAccessibilityActionProp, + ...props + }, + ref, + ) => { + const triggerRef = React.useRef(null); + const { open, onOpenChange, relativeTo, setPressPosition } = + useRootContext(); + + React.useImperativeHandle( + ref, + () => { + if (!triggerRef.current) { + return new View({}); + } + return triggerRef.current; + }, + [triggerRef.current], + ); + + function onLongPress(ev: GestureResponderEvent) { + if (disabled) return; + if (relativeTo === "longPress") { + setPressPosition({ + width: 0, + pageX: ev.nativeEvent.pageX, + pageY: ev.nativeEvent.pageY, + height: 0, + }); + } + if (relativeTo === "trigger") { + triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { + setPressPosition({ width, pageX, pageY: pageY, height }); + }); + } + onOpenChange(!open); + onLongPressProp?.(ev); + } + + function onAccessibilityAction(event: AccessibilityActionEvent) { + if (disabled) return; + if (event.nativeEvent.actionName === "longpress") { + setPressPosition({ + width: 0, + pageX: 0, + pageY: 0, + height: 0, + }); + const newValue = !open; + onOpenChange(newValue); + } + onAccessibilityActionProp?.(event); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeContextMenu'; +Trigger.displayName = "TriggerNativeContextMenu"; /** * @warning when using a custom ``, you will have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: ContextMenuPortalProps) { - const value = useRootContext(); - - if (!value.pressPosition) { - return null; - } - - if (!forceMount) { - if (!value.open) { - return null; - } - } - - return ( - - {children} - - ); + const value = useRootContext(); + + if (!value.pressPosition) { + return null; + } + + if (!forceMount) { + if (!value.open) { + return null; + } + } + + return ( + + {children} + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => { - const { open, onOpenChange, setContentLayout, setPressPosition } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setPressPosition(null); - setContentLayout(null); - onOpenChange(false); - } - OnPressProp?.(ev); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & ContextMenuOverlayProps +>( + ( + { + asChild, + forceMount, + onPress: OnPressProp, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { open, onOpenChange, setContentLayout, setPressPosition } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setPressPosition(null); + setContentLayout(null); + onOpenChange(false); + } + OnPressProp?.(ev); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, ); -Overlay.displayName = 'OverlayNativeContextMenu'; - -const Content = React.forwardRef( - ( - { - asChild = false, - forceMount, - align = 'start', - side = 'bottom', - sideOffset = 0, - alignOffset = 0, - avoidCollisions = true, - onLayout: onLayoutProp, - insets, - style, - disablePositioningStyle, - ...props - }, - ref - ) => { - const { - open, - onOpenChange, - contentLayout, - nativeID, - pressPosition, - setContentLayout, - setPressPosition, - } = useRootContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setPressPosition(null); - setContentLayout(null); - onOpenChange(false); - return true; - }); - - return () => { - setContentLayout(null); - backHandler.remove(); - }; - }, []); - - const positionStyle = useRelativePosition({ - align, - avoidCollisions, - triggerPosition: pressPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, - }); - - function onLayout(event: LayoutChangeEvent) { - setContentLayout(event.nativeEvent.layout); - onLayoutProp?.(event); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } +Overlay.displayName = "OverlayNativeContextMenu"; + +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & PositionedContentProps +>( + ( + { + asChild = false, + forceMount, + align = "start", + side = "bottom", + sideOffset = 0, + alignOffset = 0, + avoidCollisions = true, + onLayout: onLayoutProp, + insets, + style, + disablePositioningStyle, + ...props + }, + ref, + ) => { + const { + open, + onOpenChange, + contentLayout, + nativeID, + pressPosition, + setContentLayout, + setPressPosition, + } = useRootContext(); + + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setPressPosition(null); + setContentLayout(null); + onOpenChange(false); + return true; + }, + ); + + return () => { + setContentLayout(null); + backHandler.remove(); + }; + }, []); + + const positionStyle = useRelativePosition({ + align, + avoidCollisions, + triggerPosition: pressPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, + }); + + function onLayout(event: LayoutChangeEvent) { + setContentLayout(event.nativeEvent.layout); + onLayoutProp?.(event); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); + }, ); -Content.displayName = 'ContentNativeContextMenu'; - -const Item = React.forwardRef( - ( - { asChild, textValue, onPress: onPressProp, disabled = false, closeOnPress = true, ...props }, - ref - ) => { - const { onOpenChange, setContentLayout, setPressPosition } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setPressPosition(null); - setContentLayout(null); - onOpenChange(false); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } +Content.displayName = "ContentNativeContextMenu"; + +const Item = React.forwardRef< + PressableRef, + SlottablePressableProps & ContextMenuItemProps +>( + ( + { + asChild, + textValue, + onPress: onPressProp, + disabled = false, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { onOpenChange, setContentLayout, setPressPosition } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setPressPosition(null); + setContentLayout(null); + onOpenChange(false); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Item.displayName = 'ItemNativeContextMenu'; +Item.displayName = "ItemNativeContextMenu"; -const Group = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); +const Group = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); -Group.displayName = 'GroupNativeContextMenu'; +Group.displayName = "GroupNativeContextMenu"; -const Label = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Text : Text; - return ; -}); +const Label = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Text : Text; + return ; + }, +); -Label.displayName = 'LabelNativeContextMenu'; +Label.displayName = "LabelNativeContextMenu"; type FormItemContext = - | { checked: boolean } - | { - value: string | undefined; - onValueChange: (value: string) => void; - }; + | { checked: boolean } + | { + value: string | undefined; + onValueChange: (value: string) => void; + }; const FormItemContext = React.createContext(null); const CheckboxItem = React.forwardRef< - PressableRef, - SlottablePressableProps & ContextMenuCheckboxItemProps + PressableRef, + SlottablePressableProps & ContextMenuCheckboxItemProps >( - ( - { - asChild, - checked, - onCheckedChange, - textValue, - onPress: onPressProp, - closeOnPress = true, - disabled = false, - ...props - }, - ref - ) => { - const { onOpenChange, setContentLayout, setPressPosition, nativeID } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - onCheckedChange(!checked); - if (closeOnPress) { - setPressPosition(null); - setContentLayout(null); - onOpenChange(false); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } + ( + { + asChild, + checked, + onCheckedChange, + textValue, + onPress: onPressProp, + closeOnPress = true, + disabled = false, + ...props + }, + ref, + ) => { + const { onOpenChange, setContentLayout, setPressPosition, nativeID } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + onCheckedChange(!checked); + if (closeOnPress) { + setPressPosition(null); + setContentLayout(null); + onOpenChange(false); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -CheckboxItem.displayName = 'CheckboxItemNativeContextMenu'; +CheckboxItem.displayName = "CheckboxItemNativeContextMenu"; function useFormItemContext() { - const context = React.useContext(FormItemContext); - if (!context) { - throw new Error( - 'CheckboxItem or RadioItem compound components cannot be rendered outside of a CheckboxItem or RadioItem component' - ); - } - return context; + const context = React.useContext(FormItemContext); + if (!context) { + throw new Error( + "CheckboxItem or RadioItem compound components cannot be rendered outside of a CheckboxItem or RadioItem component", + ); + } + return context; } -const RadioGroup = React.forwardRef( - ({ asChild, value, onValueChange, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } -); +const RadioGroup = React.forwardRef< + ViewRef, + SlottableViewProps & ContextMenuRadioGroupProps +>(({ asChild, value, onValueChange, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + + + ); +}); -RadioGroup.displayName = 'RadioGroupNativeContextMenu'; +RadioGroup.displayName = "RadioGroupNativeContextMenu"; type BothFormItemContext = Exclude & { - checked: boolean; + checked: boolean; }; const RadioItemContext = React.createContext({} as { itemValue: string }); const RadioItem = React.forwardRef< - PressableRef, - SlottablePressableProps & ContextMenuRadioItemProps + PressableRef, + SlottablePressableProps & ContextMenuRadioItemProps >( - ( - { - asChild, - value: itemValue, - textValue, - onPress: onPressProp, - disabled = false, - closeOnPress = true, - ...props - }, - ref - ) => { - const { onOpenChange, setContentLayout, setPressPosition } = useRootContext(); - - const { value, onValueChange } = useFormItemContext() as BothFormItemContext; - function onPress(ev: GestureResponderEvent) { - onValueChange(itemValue); - if (closeOnPress) { - setPressPosition(null); - setContentLayout(null); - onOpenChange(false); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } + ( + { + asChild, + value: itemValue, + textValue, + onPress: onPressProp, + disabled = false, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { onOpenChange, setContentLayout, setPressPosition } = + useRootContext(); + + const { value, onValueChange } = + useFormItemContext() as BothFormItemContext; + function onPress(ev: GestureResponderEvent) { + onValueChange(itemValue); + if (closeOnPress) { + setPressPosition(null); + setContentLayout(null); + onOpenChange(false); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -RadioItem.displayName = 'RadioItemNativeContextMenu'; +RadioItem.displayName = "RadioItemNativeContextMenu"; function useItemIndicatorContext() { - return React.useContext(RadioItemContext); + return React.useContext(RadioItemContext); } -const ItemIndicator = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { itemValue } = useItemIndicatorContext(); - const { checked, value } = useFormItemContext() as BothFormItemContext; - - if (!forceMount) { - if (itemValue == null && !checked) { - return null; - } - if (value !== itemValue) { - return null; - } - } - const Component = asChild ? Slot.View : View; - return ; - } -); - -ItemIndicator.displayName = 'ItemIndicatorNativeContextMenu'; +const ItemIndicator = React.forwardRef< + ViewRef, + SlottableViewProps & ForceMountable +>(({ asChild, forceMount, ...props }, ref) => { + const { itemValue } = useItemIndicatorContext(); + const { checked, value } = useFormItemContext() as BothFormItemContext; + + if (!forceMount) { + if (itemValue == null && !checked) { + return null; + } + if (value !== itemValue) { + return null; + } + } + const Component = asChild ? Slot.View : View; + return ; +}); -const Separator = React.forwardRef( - ({ asChild, decorative, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; - } -); +ItemIndicator.displayName = "ItemIndicatorNativeContextMenu"; + +const Separator = React.forwardRef< + ViewRef, + SlottableViewProps & ContextMenuSeparatorProps +>(({ asChild, decorative, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -Separator.displayName = 'SeparatorNativeContextMenu'; +Separator.displayName = "SeparatorNativeContextMenu"; const SubContext = React.createContext<{ - nativeID: string; - open: boolean; - onOpenChange: (value: boolean) => void; + nativeID: string; + open: boolean; + onOpenChange: (value: boolean) => void; } | null>(null); const Sub = React.forwardRef( - ({ asChild, open, onOpenChange, ...props }, ref) => { - const nativeID = React.useId(); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ({ asChild, open, onOpenChange, ...props }, ref) => { + const nativeID = React.useId(); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Sub.displayName = 'SubNativeContextMenu'; +Sub.displayName = "SubNativeContextMenu"; function useSubContext() { - const context = React.useContext(SubContext); - if (!context) { - throw new Error('Sub compound components cannot be rendered outside of a Sub component'); - } - return context; + const context = React.useContext(SubContext); + if (!context) { + throw new Error( + "Sub compound components cannot be rendered outside of a Sub component", + ); + } + return context; } const SubTrigger = React.forwardRef< - PressableRef, - SlottablePressableProps & ContextMenuSubTriggerProps ->(({ asChild, textValue, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { nativeID, open, onOpenChange } = useSubContext(); - - function onPress(ev: GestureResponderEvent) { - onOpenChange(!open); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); -}); + PressableRef, + SlottablePressableProps & ContextMenuSubTriggerProps +>( + ( + { asChild, textValue, onPress: onPressProp, disabled = false, ...props }, + ref, + ) => { + const { nativeID, open, onOpenChange } = useSubContext(); + + function onPress(ev: GestureResponderEvent) { + onOpenChange(!open); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, +); -SubTrigger.displayName = 'SubTriggerNativeContextMenu'; +SubTrigger.displayName = "SubTriggerNativeContextMenu"; -const SubContent = React.forwardRef( - ({ asChild = false, forceMount, ...props }, ref) => { - const { open, nativeID } = useSubContext(); +const SubContent = React.forwardRef< + PressableRef, + SlottablePressableProps & ForceMountable +>(({ asChild = false, forceMount, ...props }, ref) => { + const { open, nativeID } = useSubContext(); - if (!forceMount) { - if (!open) { - return null; - } - } + if (!forceMount) { + if (!open) { + return null; + } + } - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } -); + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); +}); -Content.displayName = 'ContentNativeContextMenu'; +Content.displayName = "ContentNativeContextMenu"; export { - CheckboxItem, - Content, - Group, - Item, - ItemIndicator, - Label, - Overlay, - Portal, - RadioGroup, - RadioItem, - Root, - Separator, - Sub, - SubContent, - SubTrigger, - Trigger, - useRootContext, - useSubContext, + CheckboxItem, + Content, + Group, + Item, + ItemIndicator, + Label, + Overlay, + Portal, + RadioGroup, + RadioItem, + Root, + Separator, + Sub, + SubContent, + SubTrigger, + Trigger, + useRootContext, + useSubContext, }; function onStartShouldSetResponder() { - return true; + return true; } diff --git a/apps/native/src/components/primitives/context-menu/types.ts b/apps/native/src/components/primitives/context-menu/types.ts index fce7e95..014ac48 100644 --- a/apps/native/src/components/primitives/context-menu/types.ts +++ b/apps/native/src/components/primitives/context-menu/types.ts @@ -1,82 +1,82 @@ -import { ForceMountable } from '~/components/primitives/types'; +import { ForceMountable } from "~/components/primitives/types"; interface ContextMenuRootProps { - /** - * Platform: NATIVE ONLY - */ - open: boolean; - onOpenChange: (value: boolean) => void; + /** + * Platform: NATIVE ONLY + */ + open: boolean; + onOpenChange: (value: boolean) => void; - /** - * Platform: NATIVE ONLY - */ - relativeTo?: 'longPress' | 'trigger'; + /** + * Platform: NATIVE ONLY + */ + relativeTo?: "longPress" | "trigger"; } interface ContextMenuPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } interface ContextMenuOverlayProps extends ForceMountable { - /** - * Platform: NATIVE ONLY - */ - closeOnPress?: boolean; + /** + * Platform: NATIVE ONLY + */ + closeOnPress?: boolean; } interface ContextMenuItemProps { - textValue?: string; - closeOnPress?: boolean; + textValue?: string; + closeOnPress?: boolean; } interface ContextMenuCheckboxItemProps { - checked: boolean; - onCheckedChange: (checked: boolean) => void; - closeOnPress?: boolean; - textValue?: string; + checked: boolean; + onCheckedChange: (checked: boolean) => void; + closeOnPress?: boolean; + textValue?: string; } interface ContextMenuRadioGroupProps { - value: string | undefined; - onValueChange: (value: string) => void; + value: string | undefined; + onValueChange: (value: string) => void; } interface ContextMenuRadioItemProps { - value: string; - textValue?: string; - closeOnPress?: boolean; + value: string; + textValue?: string; + closeOnPress?: boolean; } interface ContextMenuSeparatorProps { - decorative?: boolean; + decorative?: boolean; } interface ContextMenuSubProps { - open: boolean; - onOpenChange: (value: boolean) => void; + open: boolean; + onOpenChange: (value: boolean) => void; } interface ContextMenuSubTriggerProps { - textValue?: string; + textValue?: string; } export type { - ContextMenuCheckboxItemProps, - ContextMenuItemProps, - ContextMenuOverlayProps, - ContextMenuPortalProps, - ContextMenuRadioGroupProps, - ContextMenuRadioItemProps, - ContextMenuRootProps, - ContextMenuSeparatorProps, - ContextMenuSubProps, - ContextMenuSubTriggerProps, + ContextMenuCheckboxItemProps, + ContextMenuItemProps, + ContextMenuOverlayProps, + ContextMenuPortalProps, + ContextMenuRadioGroupProps, + ContextMenuRadioItemProps, + ContextMenuRootProps, + ContextMenuSeparatorProps, + ContextMenuSubProps, + ContextMenuSubTriggerProps, }; diff --git a/apps/native/src/components/primitives/dialog/index.tsx b/apps/native/src/components/primitives/dialog/index.tsx index f8e82ca..49767b0 100644 --- a/apps/native/src/components/primitives/dialog/index.tsx +++ b/apps/native/src/components/primitives/dialog/index.tsx @@ -1,211 +1,260 @@ -import { useControllableState } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; +import { useControllableState } from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - PressableRef, - SlottablePressableProps, - SlottableTextProps, - SlottableViewProps, - TextRef, - ViewRef, -} from '~/components/primitives/types'; -import * as React from 'react'; -import { BackHandler, GestureResponderEvent, Pressable, Text, View } from 'react-native'; + PressableRef, + SlottablePressableProps, + SlottableTextProps, + SlottableViewProps, + TextRef, + ViewRef, +} from "~/components/primitives/types"; +import * as React from "react"; +import { + BackHandler, + GestureResponderEvent, + Pressable, + Text, + View, +} from "react-native"; import type { - DialogContentProps, - DialogOverlayProps, - DialogPortalProps, - DialogRootProps, - RootContext, -} from './types'; + DialogContentProps, + DialogOverlayProps, + DialogPortalProps, + DialogRootProps, + RootContext, +} from "./types"; -const DialogContext = React.createContext<(RootContext & { nativeID: string }) | null>(null); +const DialogContext = React.createContext< + (RootContext & { nativeID: string }) | null +>(null); const Root = React.forwardRef( - ({ asChild, open: openProp, defaultOpen, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => { - const nativeID = React.useId(); - const [open = false, onOpenChange] = useControllableState({ - prop: openProp, - defaultProp: defaultOpen, - onChange: onOpenChangeProp, - }); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ( + { + asChild, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + ...viewProps + }, + ref, + ) => { + const nativeID = React.useId(); + const [open = false, onOpenChange] = useControllableState({ + prop: openProp, + defaultProp: defaultOpen, + onChange: onOpenChangeProp, + }); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeDialog'; +Root.displayName = "RootNativeDialog"; function useRootContext() { - const context = React.useContext(DialogContext); - if (!context) { - throw new Error('Dialog compound components cannot be rendered outside the Dialog component'); - } - return context; + const context = React.useContext(DialogContext); + if (!context) { + throw new Error( + "Dialog compound components cannot be rendered outside the Dialog component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { open, onOpenChange } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - const newValue = !open; - onOpenChange(newValue); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const { open, onOpenChange } = useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + const newValue = !open; + onOpenChange(newValue); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeDialog'; +Trigger.displayName = "TriggerNativeDialog"; /** * @warning when using a custom ``, you might have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: DialogPortalProps) { - const value = useRootContext(); - - if (!forceMount) { - if (!value.open) { - return null; - } - } - - return ( - - {children} - - ); + const value = useRootContext(); + + if (!forceMount) { + if (!value.open) { + return null; + } + } + + return ( + + {children} + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, closeOnPress = true, onPress: OnPressProp, ...props }, ref) => { - const { open, onOpenChange } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - onOpenChange(!open); - } - OnPressProp?.(ev); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & DialogOverlayProps +>( + ( + { + asChild, + forceMount, + closeOnPress = true, + onPress: OnPressProp, + ...props + }, + ref, + ) => { + const { open, onOpenChange } = useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + onOpenChange(!open); + } + OnPressProp?.(ev); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, ); -Overlay.displayName = 'OverlayNativeDialog'; - -const Content = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { open, nativeID, onOpenChange } = useRootContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - onOpenChange(false); - return true; - }); - - return () => { - backHandler.remove(); - }; - }, []); - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } -); +Overlay.displayName = "OverlayNativeDialog"; + +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & DialogContentProps +>(({ asChild, forceMount, ...props }, ref) => { + const { open, nativeID, onOpenChange } = useRootContext(); + + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + onOpenChange(false); + return true; + }, + ); + + return () => { + backHandler.remove(); + }; + }, []); + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -Content.displayName = 'ContentNativeDialog'; +Content.displayName = "ContentNativeDialog"; const Close = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { onOpenChange } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - onOpenChange(false); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const { onOpenChange } = useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + onOpenChange(false); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Close.displayName = 'CloseNativeDialog'; +Close.displayName = "CloseNativeDialog"; const Title = React.forwardRef((props, ref) => { - const { nativeID } = useRootContext(); - return ; + const { nativeID } = useRootContext(); + return ( + + ); }); -Title.displayName = 'TitleNativeDialog'; +Title.displayName = "TitleNativeDialog"; -const Description = React.forwardRef((props, ref) => { - const { nativeID } = useRootContext(); - return ; -}); +const Description = React.forwardRef( + (props, ref) => { + const { nativeID } = useRootContext(); + return ; + }, +); -Description.displayName = 'DescriptionNativeDialog'; +Description.displayName = "DescriptionNativeDialog"; -export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext }; +export { + Close, + Content, + Description, + Overlay, + Portal, + Root, + Title, + Trigger, + useRootContext, +}; function onStartShouldSetResponder() { - return true; + return true; } diff --git a/apps/native/src/components/primitives/dialog/types.ts b/apps/native/src/components/primitives/dialog/types.ts index 439a868..4fc6338 100644 --- a/apps/native/src/components/primitives/dialog/types.ts +++ b/apps/native/src/components/primitives/dialog/types.ts @@ -1,60 +1,60 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; type RootContext = { - open: boolean; - onOpenChange: (value: boolean) => void; + open: boolean; + onOpenChange: (value: boolean) => void; }; type DialogRootProps = { - open?: boolean; - defaultOpen?: boolean; - onOpenChange?: (value: boolean) => void; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (value: boolean) => void; }; interface DialogPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } type DialogOverlayProps = ForceMountable & { - /** - * Platform: NATIVE ONLY - default: true - */ - closeOnPress?: boolean; + /** + * Platform: NATIVE ONLY - default: true + */ + closeOnPress?: boolean; }; type DialogContentProps = ForceMountable & { - /** - * Platform: WEB ONLY - */ - onOpenAutoFocus?: (ev: Event) => void; - /** - * Platform: WEB ONLY - */ - onCloseAutoFocus?: (ev: Event) => void; - /** - * Platform: WEB ONLY - */ - onEscapeKeyDown?: (ev: Event) => void; - /** - * Platform: WEB ONLY - */ - onInteractOutside?: (ev: Event) => void; - /** - * Platform: WEB ONLY - */ - onPointerDownOutside?: (ev: Event) => void; + /** + * Platform: WEB ONLY + */ + onOpenAutoFocus?: (ev: Event) => void; + /** + * Platform: WEB ONLY + */ + onCloseAutoFocus?: (ev: Event) => void; + /** + * Platform: WEB ONLY + */ + onEscapeKeyDown?: (ev: Event) => void; + /** + * Platform: WEB ONLY + */ + onInteractOutside?: (ev: Event) => void; + /** + * Platform: WEB ONLY + */ + onPointerDownOutside?: (ev: Event) => void; }; export type { - DialogContentProps, - DialogOverlayProps, - DialogPortalProps, - DialogRootProps, - RootContext, + DialogContentProps, + DialogOverlayProps, + DialogPortalProps, + DialogRootProps, + RootContext, }; diff --git a/apps/native/src/components/primitives/dropdown-menu/index.tsx b/apps/native/src/components/primitives/dropdown-menu/index.tsx index 279fbfb..e0115e4 100644 --- a/apps/native/src/components/primitives/dropdown-menu/index.tsx +++ b/apps/native/src/components/primitives/dropdown-menu/index.tsx @@ -1,584 +1,647 @@ -import * as React from 'react'; +import * as React from "react"; import { - BackHandler, - Pressable, - Text, - View, - type GestureResponderEvent, - type LayoutChangeEvent, - type LayoutRectangle, -} from 'react-native'; -import { useRelativePosition, type LayoutPosition } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; + BackHandler, + Pressable, + Text, + View, + type GestureResponderEvent, + type LayoutChangeEvent, + type LayoutRectangle, +} from "react-native"; +import { + useRelativePosition, + type LayoutPosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - ForceMountable, - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableTextProps, - SlottableViewProps, - TextRef, - ViewRef, -} from '~/components/primitives/types'; + ForceMountable, + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableTextProps, + SlottableViewProps, + TextRef, + ViewRef, +} from "~/components/primitives/types"; import type { - DropdownMenuCheckboxItemProps, - DropdownMenuItemProps, - DropdownMenuOverlayProps, - DropdownMenuPortalProps, - DropdownMenuRadioGroupProps, - DropdownMenuRadioItemProps, - DropdownMenuRootProps, - DropdownMenuSeparatorProps, - DropdownMenuSubProps, - DropdownMenuSubTriggerProps, -} from './types'; + DropdownMenuCheckboxItemProps, + DropdownMenuItemProps, + DropdownMenuOverlayProps, + DropdownMenuPortalProps, + DropdownMenuRadioGroupProps, + DropdownMenuRadioItemProps, + DropdownMenuRootProps, + DropdownMenuSeparatorProps, + DropdownMenuSubProps, + DropdownMenuSubTriggerProps, +} from "./types"; interface IRootContext extends DropdownMenuRootProps { - triggerPosition: LayoutPosition | null; - setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; - contentLayout: LayoutRectangle | null; - setContentLayout: (contentLayout: LayoutRectangle | null) => void; - nativeID: string; + triggerPosition: LayoutPosition | null; + setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; + contentLayout: LayoutRectangle | null; + setContentLayout: (contentLayout: LayoutRectangle | null) => void; + nativeID: string; } const RootContext = React.createContext(null); -const Root = React.forwardRef( - ({ asChild, open, onOpenChange, ...viewProps }, ref) => { - const nativeID = React.useId(); - const [triggerPosition, setTriggerPosition] = React.useState(null); - const [contentLayout, setContentLayout] = React.useState(null); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } -); +const Root = React.forwardRef< + ViewRef, + SlottableViewProps & DropdownMenuRootProps +>(({ asChild, open, onOpenChange, ...viewProps }, ref) => { + const nativeID = React.useId(); + const [triggerPosition, setTriggerPosition] = + React.useState(null); + const [contentLayout, setContentLayout] = + React.useState(null); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); +}); -Root.displayName = 'RootNativeDropdownMenu'; +Root.displayName = "RootNativeDropdownMenu"; function useRootContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error( - 'DropdownMenu compound components cannot be rendered outside the DropdownMenu component' - ); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "DropdownMenu compound components cannot be rendered outside the DropdownMenu component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const triggerRef = React.useRef(null); - const { open, onOpenChange, setTriggerPosition } = useRootContext(); - - React.useImperativeHandle( - ref, - () => { - if (!triggerRef.current) { - return new View({}); - } - return triggerRef.current; - }, - [triggerRef.current] - ); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { - setTriggerPosition({ width, pageX, pageY: pageY, height }); - }); - const newValue = !open; - onOpenChange(newValue); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const triggerRef = React.useRef(null); + const { open, onOpenChange, setTriggerPosition } = useRootContext(); + + React.useImperativeHandle( + ref, + () => { + if (!triggerRef.current) { + return new View({}); + } + return triggerRef.current; + }, + [triggerRef.current], + ); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { + setTriggerPosition({ width, pageX, pageY: pageY, height }); + }); + const newValue = !open; + onOpenChange(newValue); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeDropdownMenu'; +Trigger.displayName = "TriggerNativeDropdownMenu"; /** * @warning when using a custom ``, you might have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: DropdownMenuPortalProps) { - const value = useRootContext(); - - if (!value.triggerPosition) { - return null; - } - - if (!forceMount) { - if (!value.open) { - return null; - } - } - - return ( - - {children} - - ); + const value = useRootContext(); + + if (!value.triggerPosition) { + return null; + } + + if (!forceMount) { + if (!value.open) { + return null; + } + } + + return ( + + {children} + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => { - const { open, onOpenChange, setContentLayout, setTriggerPosition } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - OnPressProp?.(ev); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & DropdownMenuOverlayProps +>( + ( + { + asChild, + forceMount, + onPress: OnPressProp, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { open, onOpenChange, setContentLayout, setTriggerPosition } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + OnPressProp?.(ev); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, ); -Overlay.displayName = 'OverlayNativeDropdownMenu'; +Overlay.displayName = "OverlayNativeDropdownMenu"; /** * @info `position`, `top`, `left`, and `maxWidth` style properties are controlled internally. Opt out of this behavior by setting `disablePositioningStyle` to `true`. */ -const Content = React.forwardRef( - ( - { - asChild = false, - forceMount, - align = 'start', - side = 'bottom', - sideOffset = 0, - alignOffset = 0, - avoidCollisions = true, - onLayout: onLayoutProp, - insets, - style, - disablePositioningStyle, - ...props - }, - ref - ) => { - const { - open, - onOpenChange, - nativeID, - triggerPosition, - setTriggerPosition, - contentLayout, - setContentLayout, - } = useRootContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - return true; - }); - - return () => { - setContentLayout(null); - backHandler.remove(); - }; - }, []); - - const positionStyle = useRelativePosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, - }); - - function onLayout(event: LayoutChangeEvent) { - setContentLayout(event.nativeEvent.layout); - onLayoutProp?.(event); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } +const Content = React.forwardRef< + PressableRef, + SlottablePressableProps & PositionedContentProps +>( + ( + { + asChild = false, + forceMount, + align = "start", + side = "bottom", + sideOffset = 0, + alignOffset = 0, + avoidCollisions = true, + onLayout: onLayoutProp, + insets, + style, + disablePositioningStyle, + ...props + }, + ref, + ) => { + const { + open, + onOpenChange, + nativeID, + triggerPosition, + setTriggerPosition, + contentLayout, + setContentLayout, + } = useRootContext(); + + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + return true; + }, + ); + + return () => { + setContentLayout(null); + backHandler.remove(); + }; + }, []); + + const positionStyle = useRelativePosition({ + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, + }); + + function onLayout(event: LayoutChangeEvent) { + setContentLayout(event.nativeEvent.layout); + onLayoutProp?.(event); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Content.displayName = 'ContentNativeDropdownMenu'; - -const Item = React.forwardRef( - ( - { asChild, textValue, onPress: onPressProp, disabled = false, closeOnPress = true, ...props }, - ref - ) => { - const { onOpenChange, setTriggerPosition, setContentLayout } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } +Content.displayName = "ContentNativeDropdownMenu"; + +const Item = React.forwardRef< + PressableRef, + SlottablePressableProps & DropdownMenuItemProps +>( + ( + { + asChild, + textValue, + onPress: onPressProp, + disabled = false, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { onOpenChange, setTriggerPosition, setContentLayout } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Item.displayName = 'ItemNativeDropdownMenu'; +Item.displayName = "ItemNativeDropdownMenu"; -const Group = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); +const Group = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); -Group.displayName = 'GroupNativeDropdownMenu'; +Group.displayName = "GroupNativeDropdownMenu"; -const Label = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Text : Text; - return ; -}); +const Label = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Text : Text; + return ; + }, +); -Label.displayName = 'LabelNativeDropdownMenu'; +Label.displayName = "LabelNativeDropdownMenu"; type FormItemContext = - | { checked: boolean } - | { - value: string | undefined; - onValueChange: (value: string) => void; - }; + | { checked: boolean } + | { + value: string | undefined; + onValueChange: (value: string) => void; + }; const FormItemContext = React.createContext(null); const CheckboxItem = React.forwardRef< - PressableRef, - SlottablePressableProps & DropdownMenuCheckboxItemProps + PressableRef, + SlottablePressableProps & DropdownMenuCheckboxItemProps >( - ( - { - asChild, - checked, - onCheckedChange, - textValue, - onPress: onPressProp, - closeOnPress = true, - disabled = false, - ...props - }, - ref - ) => { - const { onOpenChange, setContentLayout, setTriggerPosition, nativeID } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - onCheckedChange(!checked); - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } + ( + { + asChild, + checked, + onCheckedChange, + textValue, + onPress: onPressProp, + closeOnPress = true, + disabled = false, + ...props + }, + ref, + ) => { + const { onOpenChange, setContentLayout, setTriggerPosition, nativeID } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + onCheckedChange(!checked); + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -CheckboxItem.displayName = 'CheckboxItemNativeDropdownMenu'; +CheckboxItem.displayName = "CheckboxItemNativeDropdownMenu"; function useFormItemContext() { - const context = React.useContext(FormItemContext); - if (!context) { - throw new Error( - 'CheckboxItem or RadioItem compound components cannot be rendered outside of a CheckboxItem or RadioItem component' - ); - } - return context; + const context = React.useContext(FormItemContext); + if (!context) { + throw new Error( + "CheckboxItem or RadioItem compound components cannot be rendered outside of a CheckboxItem or RadioItem component", + ); + } + return context; } -const RadioGroup = React.forwardRef( - ({ asChild, value, onValueChange, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } -); +const RadioGroup = React.forwardRef< + ViewRef, + SlottableViewProps & DropdownMenuRadioGroupProps +>(({ asChild, value, onValueChange, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + + + ); +}); -RadioGroup.displayName = 'RadioGroupNativeDropdownMenu'; +RadioGroup.displayName = "RadioGroupNativeDropdownMenu"; type BothFormItemContext = Exclude & { - checked: boolean; + checked: boolean; }; const RadioItemContext = React.createContext({} as { itemValue: string }); const RadioItem = React.forwardRef< - PressableRef, - SlottablePressableProps & DropdownMenuRadioItemProps + PressableRef, + SlottablePressableProps & DropdownMenuRadioItemProps >( - ( - { - asChild, - value: itemValue, - textValue, - onPress: onPressProp, - disabled = false, - closeOnPress = true, - ...props - }, - ref - ) => { - const { onOpenChange, setContentLayout, setTriggerPosition } = useRootContext(); - - const { value, onValueChange } = useFormItemContext() as BothFormItemContext; - function onPress(ev: GestureResponderEvent) { - onValueChange(itemValue); - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } + ( + { + asChild, + value: itemValue, + textValue, + onPress: onPressProp, + disabled = false, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { onOpenChange, setContentLayout, setTriggerPosition } = + useRootContext(); + + const { value, onValueChange } = + useFormItemContext() as BothFormItemContext; + function onPress(ev: GestureResponderEvent) { + onValueChange(itemValue); + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -RadioItem.displayName = 'RadioItemNativeDropdownMenu'; +RadioItem.displayName = "RadioItemNativeDropdownMenu"; function useItemIndicatorContext() { - return React.useContext(RadioItemContext); + return React.useContext(RadioItemContext); } -const ItemIndicator = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { itemValue } = useItemIndicatorContext(); - const { checked, value } = useFormItemContext() as BothFormItemContext; - - if (!forceMount) { - if (itemValue == null && !checked) { - return null; - } - if (value !== itemValue) { - return null; - } - } - const Component = asChild ? Slot.View : View; - return ; - } -); - -ItemIndicator.displayName = 'ItemIndicatorNativeDropdownMenu'; +const ItemIndicator = React.forwardRef< + ViewRef, + SlottableViewProps & ForceMountable +>(({ asChild, forceMount, ...props }, ref) => { + const { itemValue } = useItemIndicatorContext(); + const { checked, value } = useFormItemContext() as BothFormItemContext; + + if (!forceMount) { + if (itemValue == null && !checked) { + return null; + } + if (value !== itemValue) { + return null; + } + } + const Component = asChild ? Slot.View : View; + return ; +}); -const Separator = React.forwardRef( - ({ asChild, decorative, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; - } -); +ItemIndicator.displayName = "ItemIndicatorNativeDropdownMenu"; + +const Separator = React.forwardRef< + ViewRef, + SlottableViewProps & DropdownMenuSeparatorProps +>(({ asChild, decorative, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -Separator.displayName = 'SeparatorNativeDropdownMenu'; +Separator.displayName = "SeparatorNativeDropdownMenu"; const SubContext = React.createContext<{ - nativeID: string; - open: boolean; - onOpenChange: (value: boolean) => void; + nativeID: string; + open: boolean; + onOpenChange: (value: boolean) => void; } | null>(null); -const Sub = React.forwardRef( - ({ asChild, open, onOpenChange, ...props }, ref) => { - const nativeID = React.useId(); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } -); +const Sub = React.forwardRef< + ViewRef, + SlottableViewProps & DropdownMenuSubProps +>(({ asChild, open, onOpenChange, ...props }, ref) => { + const nativeID = React.useId(); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); +}); -Sub.displayName = 'SubNativeDropdownMenu'; +Sub.displayName = "SubNativeDropdownMenu"; function useSubContext() { - const context = React.useContext(SubContext); - if (!context) { - throw new Error('Sub compound components cannot be rendered outside of a Sub component'); - } - return context; + const context = React.useContext(SubContext); + if (!context) { + throw new Error( + "Sub compound components cannot be rendered outside of a Sub component", + ); + } + return context; } const SubTrigger = React.forwardRef< - PressableRef, - SlottablePressableProps & DropdownMenuSubTriggerProps ->(({ asChild, textValue, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { nativeID, open, onOpenChange } = useSubContext(); - - function onPress(ev: GestureResponderEvent) { - onOpenChange(!open); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); -}); + PressableRef, + SlottablePressableProps & DropdownMenuSubTriggerProps +>( + ( + { asChild, textValue, onPress: onPressProp, disabled = false, ...props }, + ref, + ) => { + const { nativeID, open, onOpenChange } = useSubContext(); + + function onPress(ev: GestureResponderEvent) { + onOpenChange(!open); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, +); -SubTrigger.displayName = 'SubTriggerNativeDropdownMenu'; +SubTrigger.displayName = "SubTriggerNativeDropdownMenu"; -const SubContent = React.forwardRef( - ({ asChild = false, forceMount, ...props }, ref) => { - const { open, nativeID } = useSubContext(); +const SubContent = React.forwardRef< + PressableRef, + SlottablePressableProps & ForceMountable +>(({ asChild = false, forceMount, ...props }, ref) => { + const { open, nativeID } = useSubContext(); - if (!forceMount) { - if (!open) { - return null; - } - } + if (!forceMount) { + if (!open) { + return null; + } + } - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } -); + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); +}); -Content.displayName = 'ContentNativeDropdownMenu'; +Content.displayName = "ContentNativeDropdownMenu"; export { - CheckboxItem, - Content, - Group, - Item, - ItemIndicator, - Label, - Overlay, - Portal, - RadioGroup, - RadioItem, - Root, - Separator, - Sub, - SubContent, - SubTrigger, - Trigger, - useRootContext, - useSubContext, + CheckboxItem, + Content, + Group, + Item, + ItemIndicator, + Label, + Overlay, + Portal, + RadioGroup, + RadioItem, + Root, + Separator, + Sub, + SubContent, + SubTrigger, + Trigger, + useRootContext, + useSubContext, }; diff --git a/apps/native/src/components/primitives/dropdown-menu/types.ts b/apps/native/src/components/primitives/dropdown-menu/types.ts index d6cf9a1..bb9289f 100644 --- a/apps/native/src/components/primitives/dropdown-menu/types.ts +++ b/apps/native/src/components/primitives/dropdown-menu/types.ts @@ -1,71 +1,71 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface DropdownMenuRootProps { - open: boolean; - onOpenChange: (value: boolean) => void; + open: boolean; + onOpenChange: (value: boolean) => void; } interface DropdownMenuPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } interface DropdownMenuOverlayProps extends ForceMountable { - closeOnPress?: boolean; + closeOnPress?: boolean; } interface DropdownMenuItemProps { - textValue?: string; - closeOnPress?: boolean; + textValue?: string; + closeOnPress?: boolean; } interface DropdownMenuCheckboxItemProps { - checked: boolean; - onCheckedChange: (checked: boolean) => void; - closeOnPress?: boolean; - textValue?: string; + checked: boolean; + onCheckedChange: (checked: boolean) => void; + closeOnPress?: boolean; + textValue?: string; } interface DropdownMenuRadioGroupProps { - value: string | undefined; - onValueChange: (value: string) => void; + value: string | undefined; + onValueChange: (value: string) => void; } interface DropdownMenuRadioItemProps { - value: string; - textValue?: string; - closeOnPress?: boolean; + value: string; + textValue?: string; + closeOnPress?: boolean; } interface DropdownMenuSeparatorProps { - decorative?: boolean; + decorative?: boolean; } interface DropdownMenuSubProps { - open: boolean; - onOpenChange: (value: boolean) => void; + open: boolean; + onOpenChange: (value: boolean) => void; } interface DropdownMenuSubTriggerProps { - textValue?: string; + textValue?: string; } export type { - DropdownMenuCheckboxItemProps, - DropdownMenuItemProps, - DropdownMenuOverlayProps, - DropdownMenuPortalProps, - DropdownMenuRadioGroupProps, - DropdownMenuRadioItemProps, - DropdownMenuRootProps, - DropdownMenuSeparatorProps, - DropdownMenuSubProps, - DropdownMenuSubTriggerProps, + DropdownMenuCheckboxItemProps, + DropdownMenuItemProps, + DropdownMenuOverlayProps, + DropdownMenuPortalProps, + DropdownMenuRadioGroupProps, + DropdownMenuRadioItemProps, + DropdownMenuRootProps, + DropdownMenuSeparatorProps, + DropdownMenuSubProps, + DropdownMenuSubTriggerProps, }; diff --git a/apps/native/src/components/primitives/hooks/index.ts b/apps/native/src/components/primitives/hooks/index.ts index 58c0d1b..4ed7385 100644 --- a/apps/native/src/components/primitives/hooks/index.ts +++ b/apps/native/src/components/primitives/hooks/index.ts @@ -1,3 +1,6 @@ -export { useAugmentedRef } from './useAugmentedRef'; -export { useRelativePosition, type LayoutPosition } from './useRelativePosition'; -export { useControllableState } from './useControllableState'; +export { useAugmentedRef } from "./useAugmentedRef"; +export { + useRelativePosition, + type LayoutPosition, +} from "./useRelativePosition"; +export { useControllableState } from "./useControllableState"; diff --git a/apps/native/src/components/primitives/hooks/useAugmentedRef.tsx b/apps/native/src/components/primitives/hooks/useAugmentedRef.tsx index 13a5669..8e9c536 100644 --- a/apps/native/src/components/primitives/hooks/useAugmentedRef.tsx +++ b/apps/native/src/components/primitives/hooks/useAugmentedRef.tsx @@ -1,29 +1,29 @@ -import * as React from 'react'; +import * as React from "react"; interface AugmentRefProps { - ref: React.Ref; - methods?: Record any>; - deps?: any[]; + ref: React.Ref; + methods?: Record any>; + deps?: any[]; } export function useAugmentedRef({ - ref, - methods, - deps = [], + ref, + methods, + deps = [], }: AugmentRefProps) { - const augmentedRef = React.useRef(null); - React.useImperativeHandle( - ref, - () => { - if (typeof augmentedRef === 'function' || !augmentedRef?.current) { - return {} as T; - } - return { - ...augmentedRef.current, - ...methods, - }; - }, - deps - ); - return augmentedRef; + const augmentedRef = React.useRef(null); + React.useImperativeHandle( + ref, + () => { + if (typeof augmentedRef === "function" || !augmentedRef?.current) { + return {} as T; + } + return { + ...augmentedRef.current, + ...methods, + }; + }, + deps, + ); + return augmentedRef; } diff --git a/apps/native/src/components/primitives/hooks/useControllableState.tsx b/apps/native/src/components/primitives/hooks/useControllableState.tsx index 3b42b53..942f1b1 100644 --- a/apps/native/src/components/primitives/hooks/useControllableState.tsx +++ b/apps/native/src/components/primitives/hooks/useControllableState.tsx @@ -2,74 +2,84 @@ // The code is licensed under the MIT License. // https://github.com/radix-ui/primitives/tree/main -import * as React from 'react'; +import * as React from "react"; type UseControllableStateParams = { - prop?: T | undefined; - defaultProp?: T | undefined; - onChange?: (state: T) => void; + prop?: T | undefined; + defaultProp?: T | undefined; + onChange?: (state: T) => void; }; type SetStateFn = (prevState?: T) => T; function useControllableState({ - prop, - defaultProp, - onChange = () => {}, + prop, + defaultProp, + onChange = () => {}, }: UseControllableStateParams) { - const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({ defaultProp, onChange }); - const isControlled = prop !== undefined; - const value = isControlled ? prop : uncontrolledProp; - const handleChange = useCallbackRef(onChange); + const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({ + defaultProp, + onChange, + }); + const isControlled = prop !== undefined; + const value = isControlled ? prop : uncontrolledProp; + const handleChange = useCallbackRef(onChange); - const setValue: React.Dispatch> = React.useCallback( - (nextValue) => { - if (isControlled) { - const setter = nextValue as SetStateFn; - const value = typeof nextValue === 'function' ? setter(prop) : nextValue; - if (value !== prop) handleChange(value as T); - } else { - setUncontrolledProp(nextValue); - } - }, - [isControlled, prop, setUncontrolledProp, handleChange] - ); + const setValue: React.Dispatch> = + React.useCallback( + (nextValue) => { + if (isControlled) { + const setter = nextValue as SetStateFn; + const value = + typeof nextValue === "function" ? setter(prop) : nextValue; + if (value !== prop) handleChange(value as T); + } else { + setUncontrolledProp(nextValue); + } + }, + [isControlled, prop, setUncontrolledProp, handleChange], + ); - return [value, setValue] as const; + return [value, setValue] as const; } function useUncontrolledState({ - defaultProp, - onChange, -}: Omit, 'prop'>) { - const uncontrolledState = React.useState(defaultProp); - const [value] = uncontrolledState; - const prevValueRef = React.useRef(value); - const handleChange = useCallbackRef(onChange); + defaultProp, + onChange, +}: Omit, "prop">) { + const uncontrolledState = React.useState(defaultProp); + const [value] = uncontrolledState; + const prevValueRef = React.useRef(value); + const handleChange = useCallbackRef(onChange); - React.useEffect(() => { - if (prevValueRef.current !== value) { - handleChange(value as T); - prevValueRef.current = value; - } - }, [value, prevValueRef, handleChange]); + React.useEffect(() => { + if (prevValueRef.current !== value) { + handleChange(value as T); + prevValueRef.current = value; + } + }, [value, prevValueRef, handleChange]); - return uncontrolledState; + return uncontrolledState; } /** * A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a * prop or avoid re-executing effects when passed as a dependency */ -function useCallbackRef any>(callback: T | undefined): T { - const callbackRef = React.useRef(callback); +function useCallbackRef any>( + callback: T | undefined, +): T { + const callbackRef = React.useRef(callback); - React.useEffect(() => { - callbackRef.current = callback; - }); + React.useEffect(() => { + callbackRef.current = callback; + }); - // https://github.com/facebook/react/issues/19240 - return React.useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, []); + // https://github.com/facebook/react/issues/19240 + return React.useMemo( + () => ((...args) => callbackRef.current?.(...args)) as T, + [], + ); } export { useControllableState }; diff --git a/apps/native/src/components/primitives/hooks/useRelativePosition.tsx b/apps/native/src/components/primitives/hooks/useRelativePosition.tsx index f1544be..1786c10 100644 --- a/apps/native/src/components/primitives/hooks/useRelativePosition.tsx +++ b/apps/native/src/components/primitives/hooks/useRelativePosition.tsx @@ -1,227 +1,235 @@ -import * as React from 'react'; +import * as React from "react"; import { - useWindowDimensions, - type LayoutRectangle, - type ScaledSize, - type ViewStyle, -} from 'react-native'; -import type { Insets } from '~/components/primitives/types'; + useWindowDimensions, + type LayoutRectangle, + type ScaledSize, + type ViewStyle, +} from "react-native"; +import type { Insets } from "~/components/primitives/types"; const POSITION_ABSOLUTE: ViewStyle = { - position: 'absolute', + position: "absolute", }; const HIDDEN_CONTENT: ViewStyle = { - position: 'absolute', - opacity: 0, - zIndex: -9999999, + position: "absolute", + opacity: 0, + zIndex: -9999999, }; type UseRelativePositionArgs = Omit< - GetContentStyleArgs, - 'triggerPosition' | 'contentLayout' | 'dimensions' + GetContentStyleArgs, + "triggerPosition" | "contentLayout" | "dimensions" > & { - triggerPosition: LayoutPosition | null; - contentLayout: LayoutRectangle | null; - disablePositioningStyle?: boolean; + triggerPosition: LayoutPosition | null; + contentLayout: LayoutRectangle | null; + disablePositioningStyle?: boolean; }; export function useRelativePosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, }: UseRelativePositionArgs) { - const dimensions = useWindowDimensions(); - return React.useMemo(() => { - if (disablePositioningStyle) { - return {}; - } - if (!triggerPosition || !contentLayout) { - return HIDDEN_CONTENT; - } - return getContentStyle({ - align, - avoidCollisions, - contentLayout, - side, - triggerPosition, - alignOffset, - insets, - sideOffset, - dimensions, - }); - }, [triggerPosition, contentLayout, dimensions.width, dimensions.height]); + const dimensions = useWindowDimensions(); + return React.useMemo(() => { + if (disablePositioningStyle) { + return {}; + } + if (!triggerPosition || !contentLayout) { + return HIDDEN_CONTENT; + } + return getContentStyle({ + align, + avoidCollisions, + contentLayout, + side, + triggerPosition, + alignOffset, + insets, + sideOffset, + dimensions, + }); + }, [triggerPosition, contentLayout, dimensions.width, dimensions.height]); } export interface LayoutPosition { - pageY: number; - pageX: number; - width: number; - height: number; + pageY: number; + pageX: number; + width: number; + height: number; } interface GetPositionArgs { - dimensions: ScaledSize; - avoidCollisions: boolean; - triggerPosition: LayoutPosition; - contentLayout: LayoutRectangle; - insets?: Insets; + dimensions: ScaledSize; + avoidCollisions: boolean; + triggerPosition: LayoutPosition; + contentLayout: LayoutRectangle; + insets?: Insets; } interface GetSidePositionArgs extends GetPositionArgs { - side: 'top' | 'bottom'; - sideOffset: number; + side: "top" | "bottom"; + sideOffset: number; } function getSidePosition({ - side, - triggerPosition, - contentLayout, - sideOffset, - insets, - avoidCollisions, - dimensions, + side, + triggerPosition, + contentLayout, + sideOffset, + insets, + avoidCollisions, + dimensions, }: GetSidePositionArgs) { - const insetTop = insets?.top ?? 0; - const insetBottom = insets?.bottom ?? 0; - const positionTop = triggerPosition?.pageY - sideOffset - contentLayout.height; - const positionBottom = triggerPosition.pageY + triggerPosition.height + sideOffset; - - if (!avoidCollisions) { - return { - top: side === 'top' ? positionTop : positionBottom, - }; - } - - if (side === 'top') { - return { - top: Math.max(insetTop, positionTop), - }; - } - - return { - top: Math.min(dimensions.height - insetBottom - contentLayout.height, positionBottom), - }; + const insetTop = insets?.top ?? 0; + const insetBottom = insets?.bottom ?? 0; + const positionTop = + triggerPosition?.pageY - sideOffset - contentLayout.height; + const positionBottom = + triggerPosition.pageY + triggerPosition.height + sideOffset; + + if (!avoidCollisions) { + return { + top: side === "top" ? positionTop : positionBottom, + }; + } + + if (side === "top") { + return { + top: Math.max(insetTop, positionTop), + }; + } + + return { + top: Math.min( + dimensions.height - insetBottom - contentLayout.height, + positionBottom, + ), + }; } interface GetAlignPositionArgs extends GetPositionArgs { - align: 'start' | 'center' | 'end'; - alignOffset: number; + align: "start" | "center" | "end"; + alignOffset: number; } function getAlignPosition({ - align, - avoidCollisions, - contentLayout, - triggerPosition, - alignOffset, - insets, - dimensions, + align, + avoidCollisions, + contentLayout, + triggerPosition, + alignOffset, + insets, + dimensions, }: GetAlignPositionArgs) { - const insetLeft = insets?.left ?? 0; - const insetRight = insets?.right ?? 0; - const maxContentWidth = dimensions.width - insetLeft - insetRight; - - const contentWidth = Math.min(contentLayout.width, maxContentWidth); - - let left = getLeftPosition( - align, - triggerPosition.pageX, - triggerPosition.width, - contentWidth, - alignOffset, - insetLeft, - insetRight, - dimensions - ); - - if (avoidCollisions) { - const doesCollide = left < insetLeft || left + contentWidth > dimensions.width - insetRight; - if (doesCollide) { - const spaceLeft = left - insetLeft; - const spaceRight = dimensions.width - insetRight - (left + contentWidth); - - if (spaceLeft > spaceRight && spaceLeft >= contentWidth) { - left = insetLeft; - } else if (spaceRight >= contentWidth) { - left = dimensions.width - insetRight - contentWidth; - } else { - const centeredPosition = Math.max( - insetLeft, - (dimensions.width - contentWidth - insetRight) / 2 - ); - left = centeredPosition; - } - } - } - - return { left, maxWidth: maxContentWidth }; + const insetLeft = insets?.left ?? 0; + const insetRight = insets?.right ?? 0; + const maxContentWidth = dimensions.width - insetLeft - insetRight; + + const contentWidth = Math.min(contentLayout.width, maxContentWidth); + + let left = getLeftPosition( + align, + triggerPosition.pageX, + triggerPosition.width, + contentWidth, + alignOffset, + insetLeft, + insetRight, + dimensions, + ); + + if (avoidCollisions) { + const doesCollide = + left < insetLeft || left + contentWidth > dimensions.width - insetRight; + if (doesCollide) { + const spaceLeft = left - insetLeft; + const spaceRight = dimensions.width - insetRight - (left + contentWidth); + + if (spaceLeft > spaceRight && spaceLeft >= contentWidth) { + left = insetLeft; + } else if (spaceRight >= contentWidth) { + left = dimensions.width - insetRight - contentWidth; + } else { + const centeredPosition = Math.max( + insetLeft, + (dimensions.width - contentWidth - insetRight) / 2, + ); + left = centeredPosition; + } + } + } + + return { left, maxWidth: maxContentWidth }; } function getLeftPosition( - align: 'start' | 'center' | 'end', - triggerPageX: number, - triggerWidth: number, - contentWidth: number, - alignOffset: number, - insetLeft: number, - insetRight: number, - dimensions: ScaledSize + align: "start" | "center" | "end", + triggerPageX: number, + triggerWidth: number, + contentWidth: number, + alignOffset: number, + insetLeft: number, + insetRight: number, + dimensions: ScaledSize, ) { - let left = 0; - if (align === 'start') { - left = triggerPageX; - } - if (align === 'center') { - left = triggerPageX + triggerWidth / 2 - contentWidth / 2; - } - if (align === 'end') { - left = triggerPageX + triggerWidth - contentWidth; - } - return Math.max( - insetLeft, - Math.min(left + alignOffset, dimensions.width - contentWidth - insetRight) - ); + let left = 0; + if (align === "start") { + left = triggerPageX; + } + if (align === "center") { + left = triggerPageX + triggerWidth / 2 - contentWidth / 2; + } + if (align === "end") { + left = triggerPageX + triggerWidth - contentWidth; + } + return Math.max( + insetLeft, + Math.min(left + alignOffset, dimensions.width - contentWidth - insetRight), + ); } -type GetContentStyleArgs = GetPositionArgs & GetSidePositionArgs & GetAlignPositionArgs; +type GetContentStyleArgs = GetPositionArgs & + GetSidePositionArgs & + GetAlignPositionArgs; function getContentStyle({ - align, - avoidCollisions, - contentLayout, - side, - triggerPosition, - alignOffset, - insets, - sideOffset, - dimensions, + align, + avoidCollisions, + contentLayout, + side, + triggerPosition, + alignOffset, + insets, + sideOffset, + dimensions, }: GetContentStyleArgs) { - return Object.assign( - POSITION_ABSOLUTE, - getSidePosition({ - side, - triggerPosition, - contentLayout, - sideOffset, - insets, - avoidCollisions, - dimensions, - }), - getAlignPosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - dimensions, - }) - ); + return Object.assign( + POSITION_ABSOLUTE, + getSidePosition({ + side, + triggerPosition, + contentLayout, + sideOffset, + insets, + avoidCollisions, + dimensions, + }), + getAlignPosition({ + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + dimensions, + }), + ); } diff --git a/apps/native/src/components/primitives/hover-card/index.tsx b/apps/native/src/components/primitives/hover-card/index.tsx index 2a44987..8b63ff2 100644 --- a/apps/native/src/components/primitives/hover-card/index.tsx +++ b/apps/native/src/components/primitives/hover-card/index.tsx @@ -1,271 +1,296 @@ -import * as React from 'react'; +import * as React from "react"; import { - BackHandler, - type GestureResponderEvent, - type LayoutChangeEvent, - type LayoutRectangle, - Pressable, - View, -} from 'react-native'; -import { type LayoutPosition, useControllableState, useRelativePosition } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; + BackHandler, + type GestureResponderEvent, + type LayoutChangeEvent, + type LayoutRectangle, + Pressable, + View, +} from "react-native"; +import { + type LayoutPosition, + useControllableState, + useRelativePosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; import type { - HoverCardOverlayProps, - HoverCardPortalProps, - HoverCardRootProps, - RootContext as RootContextType, -} from './types'; + HoverCardOverlayProps, + HoverCardPortalProps, + HoverCardRootProps, + RootContext as RootContextType, +} from "./types"; interface IRootContext extends RootContextType { - triggerPosition: LayoutPosition | null; - setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; - contentLayout: LayoutRectangle | null; - setContentLayout: (contentLayout: LayoutRectangle | null) => void; - nativeID: string; + triggerPosition: LayoutPosition | null; + setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; + contentLayout: LayoutRectangle | null; + setContentLayout: (contentLayout: LayoutRectangle | null) => void; + nativeID: string; } const RootContext = React.createContext(null); const Root = React.forwardRef( - ( - { - asChild, - open: openProp, - defaultOpen, - onOpenChange: onOpenChangeProp, - openDelay: _openDelay, - closeDelay: _closeDelay, - ...viewProps - }, - ref - ) => { - const nativeID = React.useId(); - const [open = false, onOpenChange] = useControllableState({ - prop: openProp, - defaultProp: defaultOpen, - onChange: onOpenChangeProp, - }); - const [triggerPosition, setTriggerPosition] = React.useState(null); - const [contentLayout, setContentLayout] = React.useState(null); + ( + { + asChild, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + openDelay: _openDelay, + closeDelay: _closeDelay, + ...viewProps + }, + ref, + ) => { + const nativeID = React.useId(); + const [open = false, onOpenChange] = useControllableState({ + prop: openProp, + defaultProp: defaultOpen, + onChange: onOpenChangeProp, + }); + const [triggerPosition, setTriggerPosition] = + React.useState(null); + const [contentLayout, setContentLayout] = + React.useState(null); - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeHoverCard'; +Root.displayName = "RootNativeHoverCard"; function useRootContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error( - 'HoverCard compound components cannot be rendered outside the HoverCard component' - ); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "HoverCard compound components cannot be rendered outside the HoverCard component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const triggerRef = React.useRef(null); - const { open, onOpenChange, setTriggerPosition } = useRootContext(); + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const triggerRef = React.useRef(null); + const { open, onOpenChange, setTriggerPosition } = useRootContext(); - React.useImperativeHandle( - ref, - () => { - if (!triggerRef.current) { - return new View({}); - } - return triggerRef.current; - }, - [triggerRef.current] - ); + React.useImperativeHandle( + ref, + () => { + if (!triggerRef.current) { + return new View({}); + } + return triggerRef.current; + }, + [triggerRef.current], + ); - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { - setTriggerPosition({ width, pageX, pageY: pageY, height }); - }); - const newValue = !open; - onOpenChange(newValue); - onPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { + setTriggerPosition({ width, pageX, pageY: pageY, height }); + }); + const newValue = !open; + onOpenChange(newValue); + onPressProp?.(ev); + } - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeHoverCard'; +Trigger.displayName = "TriggerNativeHoverCard"; /** * @warning when using a custom ``, you might have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: HoverCardPortalProps) { - const value = useRootContext(); + const value = useRootContext(); - if (!value.triggerPosition) { - return null; - } + if (!value.triggerPosition) { + return null; + } - if (!forceMount) { - if (!value.open) { - return null; - } - } + if (!forceMount) { + if (!value.open) { + return null; + } + } - return ( - - {children} - - ); + return ( + + {children} + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => { - const { open, onOpenChange, setTriggerPosition, setContentLayout } = useRootContext(); +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & HoverCardOverlayProps +>( + ( + { + asChild, + forceMount, + onPress: OnPressProp, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { open, onOpenChange, setTriggerPosition, setContentLayout } = + useRootContext(); - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - OnPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + OnPressProp?.(ev); + } - if (!forceMount) { - if (!open) { - return null; - } - } + if (!forceMount) { + if (!open) { + return null; + } + } - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, ); -Overlay.displayName = 'OverlayNativeHoverCard'; +Overlay.displayName = "OverlayNativeHoverCard"; /** * @info `position`, `top`, `left`, and `maxWidth` style properties are controlled internally. Opt out of this behavior by setting `disablePositioningStyle` to `true`. */ -const Content = React.forwardRef( - ( - { - asChild = false, - forceMount, - align = 'start', - side = 'bottom', - sideOffset = 0, - alignOffset = 0, - avoidCollisions = true, - onLayout: onLayoutProp, - insets, - style, - disablePositioningStyle, - ...props - }, - ref - ) => { - const { - open, - onOpenChange, - contentLayout, - nativeID, - setContentLayout, - setTriggerPosition, - triggerPosition, - } = useRootContext(); +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & PositionedContentProps +>( + ( + { + asChild = false, + forceMount, + align = "start", + side = "bottom", + sideOffset = 0, + alignOffset = 0, + avoidCollisions = true, + onLayout: onLayoutProp, + insets, + style, + disablePositioningStyle, + ...props + }, + ref, + ) => { + const { + open, + onOpenChange, + contentLayout, + nativeID, + setContentLayout, + setTriggerPosition, + triggerPosition, + } = useRootContext(); - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - return true; - }); + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + return true; + }, + ); - return () => { - setContentLayout(null); - backHandler.remove(); - }; - }, []); + return () => { + setContentLayout(null); + backHandler.remove(); + }; + }, []); - const positionStyle = useRelativePosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, - }); + const positionStyle = useRelativePosition({ + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, + }); - function onLayout(event: LayoutChangeEvent) { - setContentLayout(event.nativeEvent.layout); - onLayoutProp?.(event); - } + function onLayout(event: LayoutChangeEvent) { + setContentLayout(event.nativeEvent.layout); + onLayoutProp?.(event); + } - if (!forceMount) { - if (!open) { - return null; - } - } + if (!forceMount) { + if (!open) { + return null; + } + } - const Component = asChild ? Slot.View : View; - return ( - - ); - } + const Component = asChild ? Slot.View : View; + return ( + + ); + }, ); -Content.displayName = 'ContentNativeHoverCard'; +Content.displayName = "ContentNativeHoverCard"; export { Content, Overlay, Portal, Root, Trigger, useRootContext }; function onStartShouldSetResponder() { - return true; + return true; } diff --git a/apps/native/src/components/primitives/hover-card/types.ts b/apps/native/src/components/primitives/hover-card/types.ts index 6da90ad..bedeba2 100644 --- a/apps/native/src/components/primitives/hover-card/types.ts +++ b/apps/native/src/components/primitives/hover-card/types.ts @@ -1,42 +1,47 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface RootContext { - open: boolean; - onOpenChange: (value: boolean) => void; - openDelay?: number; - closeDelay?: number; + open: boolean; + onOpenChange: (value: boolean) => void; + openDelay?: number; + closeDelay?: number; } interface HoverCardRootProps { - open?: boolean; - defaultOpen?: boolean; - onOpenChange?: (value: boolean) => void; - /** - * Platform: WEB ONLY - * @default 700 - */ - openDelay?: number; - /** - * Platform: WEB ONLY - * @default 300 - */ - closeDelay?: number; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (value: boolean) => void; + /** + * Platform: WEB ONLY + * @default 700 + */ + openDelay?: number; + /** + * Platform: WEB ONLY + * @default 300 + */ + closeDelay?: number; } interface HoverCardPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } interface HoverCardOverlayProps extends ForceMountable { - closeOnPress?: boolean; + closeOnPress?: boolean; } -export type { HoverCardRootProps, HoverCardOverlayProps, HoverCardPortalProps, RootContext }; +export type { + HoverCardRootProps, + HoverCardOverlayProps, + HoverCardPortalProps, + RootContext, +}; diff --git a/apps/native/src/components/primitives/label/index.tsx b/apps/native/src/components/primitives/label/index.tsx index 40b5f7b..8c0232f 100644 --- a/apps/native/src/components/primitives/label/index.tsx +++ b/apps/native/src/components/primitives/label/index.tsx @@ -1,31 +1,32 @@ -import * as Slot from '~/components/primitives/slot'; +import * as Slot from "~/components/primitives/slot"; import type { - PressableRef, - SlottablePressableProps, - SlottableTextProps, - TextRef, -} from '~/components/primitives/types'; -import * as React from 'react'; -import { Pressable, Text as RNText } from 'react-native'; -import type { LabelRootProps, LabelTextProps } from './types'; + PressableRef, + SlottablePressableProps, + SlottableTextProps, + TextRef, +} from "~/components/primitives/types"; +import * as React from "react"; +import { Pressable, Text as RNText } from "react-native"; +import type { LabelRootProps, LabelTextProps } from "./types"; const Root = React.forwardRef< - PressableRef, - Omit & LabelRootProps + PressableRef, + Omit & + LabelRootProps >(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Pressable : Pressable; - return ; + const Component = asChild ? Slot.Pressable : Pressable; + return ; }); -Root.displayName = 'RootNativeLabel'; +Root.displayName = "RootNativeLabel"; const Text = React.forwardRef( - ({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ; - } + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ; + }, ); -Text.displayName = 'TextNativeLabel'; +Text.displayName = "TextNativeLabel"; export { Root, Text }; diff --git a/apps/native/src/components/primitives/label/types.ts b/apps/native/src/components/primitives/label/types.ts index d3b273c..7b462e5 100644 --- a/apps/native/src/components/primitives/label/types.ts +++ b/apps/native/src/components/primitives/label/types.ts @@ -1,15 +1,15 @@ -import type { ViewStyle } from 'react-native'; +import type { ViewStyle } from "react-native"; interface LabelRootProps { - children: React.ReactNode; - style?: ViewStyle; + children: React.ReactNode; + style?: ViewStyle; } interface LabelTextProps { - /** - * Equivalent to `id` so that the same value can be passed as `aria-labelledby` to the input element. - */ - nativeID: string; + /** + * Equivalent to `id` so that the same value can be passed as `aria-labelledby` to the input element. + */ + nativeID: string; } export type { LabelRootProps, LabelTextProps }; diff --git a/apps/native/src/components/primitives/menubar/index.tsx b/apps/native/src/components/primitives/menubar/index.tsx index 22f37db..01d0b0d 100644 --- a/apps/native/src/components/primitives/menubar/index.tsx +++ b/apps/native/src/components/primitives/menubar/index.tsx @@ -1,624 +1,698 @@ -import * as React from 'react'; +import * as React from "react"; import { - BackHandler, - Pressable, - Text, - View, - type GestureResponderEvent, - type LayoutChangeEvent, - type LayoutRectangle, -} from 'react-native'; -import { useRelativePosition, type LayoutPosition } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; + BackHandler, + Pressable, + Text, + View, + type GestureResponderEvent, + type LayoutChangeEvent, + type LayoutRectangle, +} from "react-native"; +import { + useRelativePosition, + type LayoutPosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - ForceMountable, - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableTextProps, - SlottableViewProps, - TextRef, - ViewRef, -} from '~/components/primitives/types'; + ForceMountable, + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableTextProps, + SlottableViewProps, + TextRef, + ViewRef, +} from "~/components/primitives/types"; import type { - MenubarCheckboxItemProps, - MenubarItemProps, - MenubarMenuProps, - MenubarOverlayProps, - MenubarPortalProps, - MenubarRadioGroupProps, - MenubarRadioItemProps, - MenubarRootProps, - MenubarSeparatorProps, - MenubarSubProps, - MenubarSubTriggerProps, -} from './types'; + MenubarCheckboxItemProps, + MenubarItemProps, + MenubarMenuProps, + MenubarOverlayProps, + MenubarPortalProps, + MenubarRadioGroupProps, + MenubarRadioItemProps, + MenubarRootProps, + MenubarSeparatorProps, + MenubarSubProps, + MenubarSubTriggerProps, +} from "./types"; interface IMenuContext extends MenubarRootProps { - triggerPosition: LayoutPosition | null; - setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; - contentLayout: LayoutRectangle | null; - setContentLayout: (contentLayout: LayoutRectangle | null) => void; - nativeID: string; + triggerPosition: LayoutPosition | null; + setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; + contentLayout: LayoutRectangle | null; + setContentLayout: (contentLayout: LayoutRectangle | null) => void; + nativeID: string; } const RootContext = React.createContext(null); const Root = React.forwardRef( - ({ asChild, value, onValueChange, ...viewProps }, ref) => { - const nativeID = React.useId(); - const [triggerPosition, setTriggerPosition] = React.useState(null); - const [contentLayout, setContentLayout] = React.useState(null); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ({ asChild, value, onValueChange, ...viewProps }, ref) => { + const nativeID = React.useId(); + const [triggerPosition, setTriggerPosition] = + React.useState(null); + const [contentLayout, setContentLayout] = + React.useState(null); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootMenubar'; +Root.displayName = "RootMenubar"; function useRootContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error('Menubar compound components cannot be rendered outside the Menubar component'); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "Menubar compound components cannot be rendered outside the Menubar component", + ); + } + return context; } const MenuContext = React.createContext(null); const Menu = React.forwardRef( - ({ asChild, value, ...viewProps }, ref) => { - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ({ asChild, value, ...viewProps }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Menu.displayName = 'MenuMenubar'; +Menu.displayName = "MenuMenubar"; function useMenuContext() { - const context = React.useContext(MenuContext); - if (!context) { - throw new Error('Menubar compound components cannot be rendered outside the Menubar component'); - } - return context; + const context = React.useContext(MenuContext); + if (!context) { + throw new Error( + "Menubar compound components cannot be rendered outside the Menubar component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const triggerRef = React.useRef(null); - const { value, onValueChange, setTriggerPosition } = useRootContext(); - const { value: menuValue } = useMenuContext(); - - React.useImperativeHandle( - ref, - () => { - if (!triggerRef.current) { - return new View({}); - } - return triggerRef.current; - }, - [triggerRef.current] - ); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { - setTriggerPosition({ width, pageX, pageY, height }); - }); - - onValueChange(menuValue === value ? undefined : menuValue); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const triggerRef = React.useRef(null); + const { value, onValueChange, setTriggerPosition } = useRootContext(); + const { value: menuValue } = useMenuContext(); + + React.useImperativeHandle( + ref, + () => { + if (!triggerRef.current) { + return new View({}); + } + return triggerRef.current; + }, + [triggerRef.current], + ); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { + setTriggerPosition({ width, pageX, pageY, height }); + }); + + onValueChange(menuValue === value ? undefined : menuValue); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerMenubar'; +Trigger.displayName = "TriggerMenubar"; /** * @warning when using a custom ``, you will have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: MenubarPortalProps) { - const menubar = useRootContext(); - const menu = useMenuContext(); - - if (!menubar.triggerPosition) { - return null; - } - - if (!forceMount) { - if (menubar.value !== menu.value) { - return null; - } - } - - return ( - - - - {children} - - - - ); + const menubar = useRootContext(); + const menu = useMenuContext(); + + if (!menubar.triggerPosition) { + return null; + } + + if (!forceMount) { + if (menubar.value !== menu.value) { + return null; + } + } + + return ( + + + + {children} + + + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => { - const { value, onValueChange, setContentLayout, setTriggerPosition } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onValueChange(undefined); - } - OnPressProp?.(ev); - } - - if (!forceMount) { - if (!value) { - return null; - } - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & MenubarOverlayProps +>( + ( + { + asChild, + forceMount, + onPress: OnPressProp, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { value, onValueChange, setContentLayout, setTriggerPosition } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onValueChange(undefined); + } + OnPressProp?.(ev); + } + + if (!forceMount) { + if (!value) { + return null; + } + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, ); -Overlay.displayName = 'OverlayMenubar'; +Overlay.displayName = "OverlayMenubar"; /** * @info `position`, `top`, `left`, and `maxWidth` style properties are controlled internally. Opt out of this behavior by setting `disablePositioningStyle` to `true`. */ -const Content = React.forwardRef( - ( - { - asChild = false, - forceMount, - align = 'start', - side = 'bottom', - sideOffset = 0, - alignOffset = 0, - avoidCollisions = true, - onLayout: onLayoutProp, - insets, - style, - disablePositioningStyle, - ...props - }, - ref - ) => { - const { - value, - onValueChange, - triggerPosition, - contentLayout, - setContentLayout, - nativeID, - setTriggerPosition, - } = useRootContext(); - const { value: menuValue } = useMenuContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setTriggerPosition(null); - setContentLayout(null); - onValueChange(undefined); - return true; - }); - - return () => { - setContentLayout(null); - backHandler.remove(); - }; - }, []); - - const positionStyle = useRelativePosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, - }); - - function onLayout(event: LayoutChangeEvent) { - setContentLayout(event.nativeEvent.layout); - onLayoutProp?.(event); - } - - if (!forceMount) { - if (value !== menuValue) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & PositionedContentProps +>( + ( + { + asChild = false, + forceMount, + align = "start", + side = "bottom", + sideOffset = 0, + alignOffset = 0, + avoidCollisions = true, + onLayout: onLayoutProp, + insets, + style, + disablePositioningStyle, + ...props + }, + ref, + ) => { + const { + value, + onValueChange, + triggerPosition, + contentLayout, + setContentLayout, + nativeID, + setTriggerPosition, + } = useRootContext(); + const { value: menuValue } = useMenuContext(); + + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setTriggerPosition(null); + setContentLayout(null); + onValueChange(undefined); + return true; + }, + ); + + return () => { + setContentLayout(null); + backHandler.remove(); + }; + }, []); + + const positionStyle = useRelativePosition({ + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, + }); + + function onLayout(event: LayoutChangeEvent) { + setContentLayout(event.nativeEvent.layout); + onLayoutProp?.(event); + } + + if (!forceMount) { + if (value !== menuValue) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); + }, ); -Content.displayName = 'ContentMenubar'; - -const Item = React.forwardRef( - ( - { asChild, textValue, onPress: onPressProp, disabled = false, closeOnPress = true, ...props }, - ref - ) => { - const { onValueChange, setContentLayout, setTriggerPosition } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onValueChange(undefined); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } +Content.displayName = "ContentMenubar"; + +const Item = React.forwardRef< + PressableRef, + SlottablePressableProps & MenubarItemProps +>( + ( + { + asChild, + textValue, + onPress: onPressProp, + disabled = false, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { onValueChange, setContentLayout, setTriggerPosition } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onValueChange(undefined); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Item.displayName = 'ItemMenubar'; +Item.displayName = "ItemMenubar"; -const Group = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); +const Group = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); -Group.displayName = 'GroupMenubar'; +Group.displayName = "GroupMenubar"; -const Label = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Text : Text; - return ; -}); +const Label = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Text : Text; + return ; + }, +); -Label.displayName = 'LabelMenubar'; +Label.displayName = "LabelMenubar"; type FormItemContext = - | { checked: boolean } - | { - value: string | undefined; - onValueChange: (value: string) => void; - }; + | { checked: boolean } + | { + value: string | undefined; + onValueChange: (value: string) => void; + }; const FormItemContext = React.createContext(null); const CheckboxItem = React.forwardRef< - PressableRef, - SlottablePressableProps & MenubarCheckboxItemProps + PressableRef, + SlottablePressableProps & MenubarCheckboxItemProps >( - ( - { - asChild, - checked, - onCheckedChange, - textValue, - onPress: onPressProp, - closeOnPress = true, - disabled = false, - ...props - }, - ref - ) => { - const { onValueChange, setTriggerPosition, setContentLayout, nativeID } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - onCheckedChange(!checked); - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onValueChange(undefined); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } + ( + { + asChild, + checked, + onCheckedChange, + textValue, + onPress: onPressProp, + closeOnPress = true, + disabled = false, + ...props + }, + ref, + ) => { + const { onValueChange, setTriggerPosition, setContentLayout, nativeID } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + onCheckedChange(!checked); + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onValueChange(undefined); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -CheckboxItem.displayName = 'CheckboxItemMenubar'; +CheckboxItem.displayName = "CheckboxItemMenubar"; function useFormItemContext() { - const context = React.useContext(FormItemContext); - if (!context) { - throw new Error( - 'CheckboxItem or RadioItem compound components cannot be rendered outside of a CheckboxItem or RadioItem component' - ); - } - return context; + const context = React.useContext(FormItemContext); + if (!context) { + throw new Error( + "CheckboxItem or RadioItem compound components cannot be rendered outside of a CheckboxItem or RadioItem component", + ); + } + return context; } -const RadioGroup = React.forwardRef( - ({ asChild, value, onValueChange, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } -); +const RadioGroup = React.forwardRef< + ViewRef, + SlottableViewProps & MenubarRadioGroupProps +>(({ asChild, value, onValueChange, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + + + ); +}); -RadioGroup.displayName = 'RadioGroupMenubar'; +RadioGroup.displayName = "RadioGroupMenubar"; type BothFormItemContext = Exclude & { - checked: boolean; + checked: boolean; }; const RadioItemContext = React.createContext({} as { itemValue: string }); -const RadioItem = React.forwardRef( - ( - { - asChild, - value: itemValue, - textValue, - onPress: onPressProp, - disabled = false, - closeOnPress = true, - ...props - }, - ref - ) => { - const { - onValueChange: onRootValueChange, - setTriggerPosition, - setContentLayout, - } = useRootContext(); - - const { value, onValueChange } = useFormItemContext() as BothFormItemContext; - function onPress(ev: GestureResponderEvent) { - onValueChange(itemValue); - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onRootValueChange(undefined); - } - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } +const RadioItem = React.forwardRef< + PressableRef, + SlottablePressableProps & MenubarRadioItemProps +>( + ( + { + asChild, + value: itemValue, + textValue, + onPress: onPressProp, + disabled = false, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { + onValueChange: onRootValueChange, + setTriggerPosition, + setContentLayout, + } = useRootContext(); + + const { value, onValueChange } = + useFormItemContext() as BothFormItemContext; + function onPress(ev: GestureResponderEvent) { + onValueChange(itemValue); + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onRootValueChange(undefined); + } + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -RadioItem.displayName = 'RadioItemMenubar'; +RadioItem.displayName = "RadioItemMenubar"; function useItemIndicatorContext() { - return React.useContext(RadioItemContext); + return React.useContext(RadioItemContext); } -const ItemIndicator = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { itemValue } = useItemIndicatorContext(); - const { checked, value } = useFormItemContext() as BothFormItemContext; - - if (!forceMount) { - if (itemValue == null && !checked) { - return null; - } - if (value !== itemValue) { - return null; - } - } - const Component = asChild ? Slot.View : View; - return ; - } -); - -ItemIndicator.displayName = 'ItemIndicatorMenubar'; +const ItemIndicator = React.forwardRef< + ViewRef, + SlottableViewProps & ForceMountable +>(({ asChild, forceMount, ...props }, ref) => { + const { itemValue } = useItemIndicatorContext(); + const { checked, value } = useFormItemContext() as BothFormItemContext; + + if (!forceMount) { + if (itemValue == null && !checked) { + return null; + } + if (value !== itemValue) { + return null; + } + } + const Component = asChild ? Slot.View : View; + return ; +}); -const Separator = React.forwardRef( - ({ asChild, decorative, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; - } -); +ItemIndicator.displayName = "ItemIndicatorMenubar"; + +const Separator = React.forwardRef< + ViewRef, + SlottableViewProps & MenubarSeparatorProps +>(({ asChild, decorative, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -Separator.displayName = 'SeparatorMenubar'; +Separator.displayName = "SeparatorMenubar"; const SubContext = React.createContext<{ - nativeID: string; - open: boolean; - onOpenChange: (value: boolean) => void; + nativeID: string; + open: boolean; + onOpenChange: (value: boolean) => void; } | null>(null); const Sub = React.forwardRef( - ({ asChild, open, onOpenChange, ...props }, ref) => { - const nativeID = React.useId(); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ({ asChild, open, onOpenChange, ...props }, ref) => { + const nativeID = React.useId(); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Sub.displayName = 'SubMenubar'; +Sub.displayName = "SubMenubar"; function useSubContext() { - const context = React.useContext(SubContext); - if (!context) { - throw new Error('Sub compound components cannot be rendered outside of a Sub component'); - } - return context; + const context = React.useContext(SubContext); + if (!context) { + throw new Error( + "Sub compound components cannot be rendered outside of a Sub component", + ); + } + return context; } -const SubTrigger = React.forwardRef( - ({ asChild, textValue, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { nativeID, open, onOpenChange } = useSubContext(); - - function onPress(ev: GestureResponderEvent) { - onOpenChange(!open); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } +const SubTrigger = React.forwardRef< + PressableRef, + SlottablePressableProps & MenubarSubTriggerProps +>( + ( + { asChild, textValue, onPress: onPressProp, disabled = false, ...props }, + ref, + ) => { + const { nativeID, open, onOpenChange } = useSubContext(); + + function onPress(ev: GestureResponderEvent) { + onOpenChange(!open); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -SubTrigger.displayName = 'SubTriggerMenubar'; +SubTrigger.displayName = "SubTriggerMenubar"; -const SubContent = React.forwardRef( - ({ asChild = false, forceMount, ...props }, ref) => { - const { open, nativeID } = useSubContext(); +const SubContent = React.forwardRef< + ViewRef, + SlottableViewProps & ForceMountable +>(({ asChild = false, forceMount, ...props }, ref) => { + const { open, nativeID } = useSubContext(); - if (!forceMount) { - if (!open) { - return null; - } - } + if (!forceMount) { + if (!open) { + return null; + } + } - const Component = asChild ? Slot.View : View; - return ; - } -); + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -SubContent.displayName = 'SubContentMenubar'; +SubContent.displayName = "SubContentMenubar"; export { - CheckboxItem, - Content, - Group, - Item, - ItemIndicator, - Label, - Menu, - Overlay, - Portal, - RadioGroup, - RadioItem, - Root, - Separator, - Sub, - SubContent, - SubTrigger, - Trigger, - useMenuContext, - useRootContext, - useSubContext, + CheckboxItem, + Content, + Group, + Item, + ItemIndicator, + Label, + Menu, + Overlay, + Portal, + RadioGroup, + RadioItem, + Root, + Separator, + Sub, + SubContent, + SubTrigger, + Trigger, + useMenuContext, + useRootContext, + useSubContext, }; function onStartShouldSetResponder() { - return true; + return true; } diff --git a/apps/native/src/components/primitives/menubar/types.ts b/apps/native/src/components/primitives/menubar/types.ts index 5774979..c618241 100644 --- a/apps/native/src/components/primitives/menubar/types.ts +++ b/apps/native/src/components/primitives/menubar/types.ts @@ -1,76 +1,76 @@ -import { ForceMountable } from '~/components/primitives/types'; +import { ForceMountable } from "~/components/primitives/types"; interface MenubarRootProps { - value: string | undefined; - onValueChange: (value: string | undefined) => void; + value: string | undefined; + onValueChange: (value: string | undefined) => void; } interface MenubarMenuProps { - value: string | undefined; + value: string | undefined; } interface MenubarPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } interface MenubarOverlayProps extends ForceMountable { - closeOnPress?: boolean; + closeOnPress?: boolean; } interface MenubarItemProps { - textValue?: string; - closeOnPress?: boolean; + textValue?: string; + closeOnPress?: boolean; } interface MenubarCheckboxItemProps { - checked: boolean; - onCheckedChange: (checked: boolean) => void; - closeOnPress?: boolean; - textValue?: string; + checked: boolean; + onCheckedChange: (checked: boolean) => void; + closeOnPress?: boolean; + textValue?: string; } interface MenubarRadioGroupProps { - value: string | undefined; - onValueChange: (value: string) => void; + value: string | undefined; + onValueChange: (value: string) => void; } interface MenubarRadioItemProps { - value: string; - textValue?: string; - closeOnPress?: boolean; + value: string; + textValue?: string; + closeOnPress?: boolean; } interface MenubarSeparatorProps { - decorative?: boolean; + decorative?: boolean; } interface MenubarSubProps { - open: boolean; - onOpenChange: (value: boolean) => void; + open: boolean; + onOpenChange: (value: boolean) => void; } interface MenubarSubTriggerProps { - textValue?: string; + textValue?: string; } export type { - MenubarCheckboxItemProps, - MenubarItemProps, - MenubarMenuProps, - MenubarOverlayProps, - MenubarPortalProps, - MenubarRadioGroupProps, - MenubarRadioItemProps, - MenubarRootProps, - MenubarSeparatorProps, - MenubarSubProps, - MenubarSubTriggerProps, + MenubarCheckboxItemProps, + MenubarItemProps, + MenubarMenuProps, + MenubarOverlayProps, + MenubarPortalProps, + MenubarRadioGroupProps, + MenubarRadioItemProps, + MenubarRootProps, + MenubarSeparatorProps, + MenubarSubProps, + MenubarSubTriggerProps, }; diff --git a/apps/native/src/components/primitives/navigation-menu/index.tsx b/apps/native/src/components/primitives/navigation-menu/index.tsx index c807697..4ad702a 100644 --- a/apps/native/src/components/primitives/navigation-menu/index.tsx +++ b/apps/native/src/components/primitives/navigation-menu/index.tsx @@ -1,315 +1,338 @@ -import * as React from 'react'; +import * as React from "react"; import { - BackHandler, - Pressable, - View, - type GestureResponderEvent, - type LayoutChangeEvent, - type LayoutRectangle, -} from 'react-native'; -import { useRelativePosition, type LayoutPosition } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; + BackHandler, + Pressable, + View, + type GestureResponderEvent, + type LayoutChangeEvent, + type LayoutRectangle, +} from "react-native"; +import { + useRelativePosition, + type LayoutPosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; import type { - NavigationMenuItemProps, - NavigationMenuLinkProps, - NavigationMenuPortalProps, - NavigationMenuRootProps, -} from './types'; + NavigationMenuItemProps, + NavigationMenuLinkProps, + NavigationMenuPortalProps, + NavigationMenuRootProps, +} from "./types"; interface INavigationMenuRootContext extends NavigationMenuRootProps { - triggerPosition: LayoutPosition | null; - setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; - contentLayout: LayoutRectangle | null; - setContentLayout: (contentLayout: LayoutRectangle | null) => void; - nativeID: string; + triggerPosition: LayoutPosition | null; + setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; + contentLayout: LayoutRectangle | null; + setContentLayout: (contentLayout: LayoutRectangle | null) => void; + nativeID: string; } -const RootContext = React.createContext(null); - -const Root = React.forwardRef( - ({ asChild, value, onValueChange, ...viewProps }, ref) => { - const nativeID = React.useId(); - const [triggerPosition, setTriggerPosition] = React.useState(null); - const [contentLayout, setContentLayout] = React.useState(null); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } +const RootContext = React.createContext( + null, ); -Root.displayName = 'RootNativeNavigationMenu'; +const Root = React.forwardRef< + ViewRef, + SlottableViewProps & NavigationMenuRootProps +>(({ asChild, value, onValueChange, ...viewProps }, ref) => { + const nativeID = React.useId(); + const [triggerPosition, setTriggerPosition] = + React.useState(null); + const [contentLayout, setContentLayout] = + React.useState(null); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); +}); + +Root.displayName = "RootNativeNavigationMenu"; function useRootContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error( - 'NavigationMenu compound components cannot be rendered outside the NavigationMenu component' - ); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "NavigationMenu compound components cannot be rendered outside the NavigationMenu component", + ); + } + return context; } -const List = React.forwardRef(({ asChild, ...viewProps }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); - -List.displayName = 'ListNativeNavigationMenu'; - -const ItemContext = React.createContext<(NavigationMenuItemProps & { nativeID: string }) | null>( - null +const List = React.forwardRef( + ({ asChild, ...viewProps }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, ); -const Item = React.forwardRef( - ({ asChild, value, ...viewProps }, ref) => { - const nativeID = React.useId(); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } -); +List.displayName = "ListNativeNavigationMenu"; + +const ItemContext = React.createContext< + (NavigationMenuItemProps & { nativeID: string }) | null +>(null); + +const Item = React.forwardRef< + ViewRef, + SlottableViewProps & NavigationMenuItemProps +>(({ asChild, value, ...viewProps }, ref) => { + const nativeID = React.useId(); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); +}); -Item.displayName = 'ItemNativeNavigationMenu'; +Item.displayName = "ItemNativeNavigationMenu"; function useItemContext() { - const context = React.useContext(ItemContext); - if (!context) { - throw new Error( - 'NavigationMenu compound components cannot be rendered outside the NavigationMenu component' - ); - } - return context; + const context = React.useContext(ItemContext); + if (!context) { + throw new Error( + "NavigationMenu compound components cannot be rendered outside the NavigationMenu component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const triggerRef = React.useRef(null); - const { value, onValueChange, setTriggerPosition } = useRootContext(); - const { value: menuValue } = useItemContext(); - - React.useImperativeHandle( - ref, - () => { - if (!triggerRef.current) { - return new View({}); - } - return triggerRef.current; - }, - [triggerRef.current] - ); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { - setTriggerPosition({ width, pageX, pageY: pageY, height }); - }); - - onValueChange(menuValue === value ? '' : menuValue); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const triggerRef = React.useRef(null); + const { value, onValueChange, setTriggerPosition } = useRootContext(); + const { value: menuValue } = useItemContext(); + + React.useImperativeHandle( + ref, + () => { + if (!triggerRef.current) { + return new View({}); + } + return triggerRef.current; + }, + [triggerRef.current], + ); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { + setTriggerPosition({ width, pageX, pageY: pageY, height }); + }); + + onValueChange(menuValue === value ? "" : menuValue); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeNavigationMenu'; +Trigger.displayName = "TriggerNativeNavigationMenu"; /** * @warning when using a custom ``, you will have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: NavigationMenuPortalProps) { - const navigationMenu = useRootContext(); - const item = useItemContext(); - - if (!navigationMenu.triggerPosition) { - return null; - } - - if (!forceMount) { - if (navigationMenu.value !== item.value) { - return null; - } - } - - return ( - - - {children} - - - ); + const navigationMenu = useRootContext(); + const item = useItemContext(); + + if (!navigationMenu.triggerPosition) { + return null; + } + + if (!forceMount) { + if (navigationMenu.value !== item.value) { + return null; + } + } + + return ( + + + {children} + + + ); } /** * @info `position`, `top`, `left`, and `maxWidth` style properties are controlled internally. Opt out of this behavior by setting `disablePositioningStyle` to `true`. */ -const Content = React.forwardRef( - ( - { - asChild = false, - forceMount, - align = 'center', - side = 'bottom', - sideOffset = 0, - alignOffset = 0, - avoidCollisions = true, - onLayout: onLayoutProp, - insets, - style, - disablePositioningStyle, - ...props - }, - ref - ) => { - const { - value, - onValueChange, - triggerPosition, - setTriggerPosition, - contentLayout, - setContentLayout, - } = useRootContext(); - const { value: menuValue, nativeID } = useItemContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setTriggerPosition(null); - setContentLayout(null); - onValueChange(''); - return true; - }); - - return () => { - setContentLayout(null); - backHandler.remove(); - }; - }, []); - - const positionStyle = useRelativePosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, - }); - - function onLayout(event: LayoutChangeEvent) { - setContentLayout(event.nativeEvent.layout); - onLayoutProp?.(event); - } - - if (!forceMount) { - if (value !== menuValue) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & PositionedContentProps +>( + ( + { + asChild = false, + forceMount, + align = "center", + side = "bottom", + sideOffset = 0, + alignOffset = 0, + avoidCollisions = true, + onLayout: onLayoutProp, + insets, + style, + disablePositioningStyle, + ...props + }, + ref, + ) => { + const { + value, + onValueChange, + triggerPosition, + setTriggerPosition, + contentLayout, + setContentLayout, + } = useRootContext(); + const { value: menuValue, nativeID } = useItemContext(); + + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setTriggerPosition(null); + setContentLayout(null); + onValueChange(""); + return true; + }, + ); + + return () => { + setContentLayout(null); + backHandler.remove(); + }; + }, []); + + const positionStyle = useRelativePosition({ + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, + }); + + function onLayout(event: LayoutChangeEvent) { + setContentLayout(event.nativeEvent.layout); + onLayoutProp?.(event); + } + + if (!forceMount) { + if (value !== menuValue) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); + }, ); -Content.displayName = 'ContentNativeNavigationMenu'; +Content.displayName = "ContentNativeNavigationMenu"; -const Link = React.forwardRef( - ({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } -); +const Link = React.forwardRef< + PressableRef, + SlottablePressableProps & NavigationMenuLinkProps +>(({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Pressable : Pressable; + return ; +}); -Link.displayName = 'LinkNativeNavigationMenu'; +Link.displayName = "LinkNativeNavigationMenu"; const Viewport = React.forwardRef< - ViewRef, - Omit, 'children'> + ViewRef, + Omit, "children"> >((props, ref) => { - return ; + return ; }); -Viewport.displayName = 'ViewportNativeNavigationMenu'; +Viewport.displayName = "ViewportNativeNavigationMenu"; -const Indicator = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); +const Indicator = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); -Indicator.displayName = 'IndicatorNativeNavigationMenu'; +Indicator.displayName = "IndicatorNativeNavigationMenu"; export { - Content, - Indicator, - Item, - Link, - List, - Portal, - Root, - Trigger, - Viewport, - useItemContext, - useRootContext, + Content, + Indicator, + Item, + Link, + List, + Portal, + Root, + Trigger, + Viewport, + useItemContext, + useRootContext, }; function onStartShouldSetResponder() { - return true; + return true; } diff --git a/apps/native/src/components/primitives/navigation-menu/types.ts b/apps/native/src/components/primitives/navigation-menu/types.ts index e58fe53..e1ac125 100644 --- a/apps/native/src/components/primitives/navigation-menu/types.ts +++ b/apps/native/src/components/primitives/navigation-menu/types.ts @@ -1,49 +1,49 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface NavigationMenuRootProps { - value: string | undefined; - onValueChange: (value: string | undefined) => void; - /** - * Platform: WEB ONLY - */ - delayDuration?: number; - /** - * Platform: WEB ONLY - */ - skipDelayDuration?: number; - /** - * Platform: WEB ONLY - */ - dir?: 'ltr' | 'rtl'; - /** - * Platform: WEB ONLY - */ - orientation?: 'horizontal' | 'vertical'; + value: string | undefined; + onValueChange: (value: string | undefined) => void; + /** + * Platform: WEB ONLY + */ + delayDuration?: number; + /** + * Platform: WEB ONLY + */ + skipDelayDuration?: number; + /** + * Platform: WEB ONLY + */ + dir?: "ltr" | "rtl"; + /** + * Platform: WEB ONLY + */ + orientation?: "horizontal" | "vertical"; } interface NavigationMenuItemProps { - value: string | undefined; + value: string | undefined; } interface NavigationMenuPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } interface NavigationMenuLinkProps { - active?: boolean; + active?: boolean; } export type { - NavigationMenuItemProps, - NavigationMenuPortalProps, - NavigationMenuRootProps, - NavigationMenuLinkProps, + NavigationMenuItemProps, + NavigationMenuPortalProps, + NavigationMenuRootProps, + NavigationMenuLinkProps, }; diff --git a/apps/native/src/components/primitives/popover/index.tsx b/apps/native/src/components/primitives/popover/index.tsx index 4e82d8d..b4918de 100644 --- a/apps/native/src/components/primitives/popover/index.tsx +++ b/apps/native/src/components/primitives/popover/index.tsx @@ -1,286 +1,323 @@ -import * as React from 'react'; +import * as React from "react"; import { - BackHandler, - type GestureResponderEvent, - type LayoutChangeEvent, - type LayoutRectangle, - Pressable, - View, -} from 'react-native'; -import { type LayoutPosition, useControllableState, useRelativePosition } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; + BackHandler, + type GestureResponderEvent, + type LayoutChangeEvent, + type LayoutRectangle, + Pressable, + View, +} from "react-native"; +import { + type LayoutPosition, + useControllableState, + useRelativePosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; import type { - PopoverOverlayProps, - PopoverPortalProps, - PopoverRootProps, - RootContext as RootContextType, -} from './types'; + PopoverOverlayProps, + PopoverPortalProps, + PopoverRootProps, + RootContext as RootContextType, +} from "./types"; interface IRootContext extends RootContextType { - triggerPosition: LayoutPosition | null; - setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; - contentLayout: LayoutRectangle | null; - setContentLayout: (contentLayout: LayoutRectangle | null) => void; - nativeID: string; + triggerPosition: LayoutPosition | null; + setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; + contentLayout: LayoutRectangle | null; + setContentLayout: (contentLayout: LayoutRectangle | null) => void; + nativeID: string; } const RootContext = React.createContext(null); const Root = React.forwardRef( - ({ asChild, open: openProp, defaultOpen, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => { - const [open = false, onOpenChange] = useControllableState({ - prop: openProp, - defaultProp: defaultOpen, - onChange: onOpenChangeProp, - }); - const nativeID = React.useId(); - const [triggerPosition, setTriggerPosition] = React.useState(null); - const [contentLayout, setContentLayout] = React.useState(null); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ( + { + asChild, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + ...viewProps + }, + ref, + ) => { + const [open = false, onOpenChange] = useControllableState({ + prop: openProp, + defaultProp: defaultOpen, + onChange: onOpenChangeProp, + }); + const nativeID = React.useId(); + const [triggerPosition, setTriggerPosition] = + React.useState(null); + const [contentLayout, setContentLayout] = + React.useState(null); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativePopover'; +Root.displayName = "RootNativePopover"; function usePopoverContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error('Popover compound components cannot be rendered outside the Popover component'); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "Popover compound components cannot be rendered outside the Popover component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const triggerRef = React.useRef(null); - const { open, onOpenChange, setTriggerPosition } = usePopoverContext(); - - React.useImperativeHandle( - ref, - () => { - if (!triggerRef.current) { - return new View({}); - } - return triggerRef.current; - }, - [triggerRef.current] - ); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { - setTriggerPosition({ width, pageX, pageY: pageY, height }); - }); - const newValue = !open; - onOpenChange(newValue); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const triggerRef = React.useRef(null); + const { open, onOpenChange, setTriggerPosition } = usePopoverContext(); + + React.useImperativeHandle( + ref, + () => { + if (!triggerRef.current) { + return new View({}); + } + return triggerRef.current; + }, + [triggerRef.current], + ); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { + setTriggerPosition({ width, pageX, pageY: pageY, height }); + }); + const newValue = !open; + onOpenChange(newValue); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativePopover'; +Trigger.displayName = "TriggerNativePopover"; /** * @warning when using a custom ``, you might have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: PopoverPortalProps) { - const value = usePopoverContext(); - - if (!value.triggerPosition) { - return null; - } - - if (!forceMount) { - if (!value.open) { - return null; - } - } - - return ( - - {children} - - ); + const value = usePopoverContext(); + + if (!value.triggerPosition) { + return null; + } + + if (!forceMount) { + if (!value.open) { + return null; + } + } + + return ( + + {children} + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => { - const { open, onOpenChange, setTriggerPosition, setContentLayout } = usePopoverContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - OnPressProp?.(ev); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & PopoverOverlayProps +>( + ( + { + asChild, + forceMount, + onPress: OnPressProp, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { open, onOpenChange, setTriggerPosition, setContentLayout } = + usePopoverContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + OnPressProp?.(ev); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, ); -Overlay.displayName = 'OverlayNativePopover'; +Overlay.displayName = "OverlayNativePopover"; /** * @info `position`, `top`, `left`, and `maxWidth` style properties are controlled internally. Opt out of this behavior by setting `disablePositioningStyle` to `true`. */ -const Content = React.forwardRef( - ( - { - asChild = false, - forceMount, - align = 'start', - side = 'bottom', - sideOffset = 0, - alignOffset = 0, - avoidCollisions = true, - onLayout: onLayoutProp, - insets, - style, - disablePositioningStyle, - ...props - }, - ref - ) => { - const { - open, - onOpenChange, - contentLayout, - nativeID, - setContentLayout, - setTriggerPosition, - triggerPosition, - } = usePopoverContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - return true; - }); - - return () => { - setContentLayout(null); - backHandler.remove(); - }; - }, []); - - const positionStyle = useRelativePosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, - }); - - function onLayout(event: LayoutChangeEvent) { - setContentLayout(event.nativeEvent.layout); - onLayoutProp?.(event); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & PositionedContentProps +>( + ( + { + asChild = false, + forceMount, + align = "start", + side = "bottom", + sideOffset = 0, + alignOffset = 0, + avoidCollisions = true, + onLayout: onLayoutProp, + insets, + style, + disablePositioningStyle, + ...props + }, + ref, + ) => { + const { + open, + onOpenChange, + contentLayout, + nativeID, + setContentLayout, + setTriggerPosition, + triggerPosition, + } = usePopoverContext(); + + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + return true; + }, + ); + + return () => { + setContentLayout(null); + backHandler.remove(); + }; + }, []); + + const positionStyle = useRelativePosition({ + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, + }); + + function onLayout(event: LayoutChangeEvent) { + setContentLayout(event.nativeEvent.layout); + onLayoutProp?.(event); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); + }, ); -Content.displayName = 'ContentNativePopover'; +Content.displayName = "ContentNativePopover"; const Close = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { onOpenChange, setContentLayout, setTriggerPosition } = usePopoverContext(); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const { onOpenChange, setContentLayout, setTriggerPosition } = + usePopoverContext(); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Close.displayName = 'CloseNativePopover'; +Close.displayName = "CloseNativePopover"; export { Close, Content, Overlay, Portal, Root, Trigger, usePopoverContext }; function onStartShouldSetResponder() { - return true; + return true; } diff --git a/apps/native/src/components/primitives/popover/types.ts b/apps/native/src/components/primitives/popover/types.ts index 26d4cec..3d72c19 100644 --- a/apps/native/src/components/primitives/popover/types.ts +++ b/apps/native/src/components/primitives/popover/types.ts @@ -1,30 +1,35 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface RootContext { - open: boolean; - onOpenChange: (value: boolean) => void; + open: boolean; + onOpenChange: (value: boolean) => void; } interface PopoverRootProps { - open?: boolean; - defaultOpen?: boolean; - onOpenChange?: (value: boolean) => void; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (value: boolean) => void; } interface PopoverPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } interface PopoverOverlayProps extends ForceMountable { - closeOnPress?: boolean; + closeOnPress?: boolean; } -export type { PopoverRootProps, PopoverPortalProps, PopoverOverlayProps, RootContext }; +export type { + PopoverRootProps, + PopoverPortalProps, + PopoverOverlayProps, + RootContext, +}; diff --git a/apps/native/src/components/primitives/portal.tsx b/apps/native/src/components/primitives/portal.tsx index a261401..a6f1d04 100644 --- a/apps/native/src/components/primitives/portal.tsx +++ b/apps/native/src/components/primitives/portal.tsx @@ -1,67 +1,67 @@ -import * as React from 'react'; -import { create } from 'zustand'; +import * as React from "react"; +import { create } from "zustand"; -const DEFAULT_PORTAL_HOST = 'INTERNAL_PRIMITIVE_DEFAULT_HOST_NAME'; +const DEFAULT_PORTAL_HOST = "INTERNAL_PRIMITIVE_DEFAULT_HOST_NAME"; type PortalMap = Map; type PortalHostMap = Map; const usePortal = create<{ map: PortalHostMap }>(() => ({ - map: new Map().set( - DEFAULT_PORTAL_HOST, - new Map() - ), + map: new Map().set( + DEFAULT_PORTAL_HOST, + new Map(), + ), })); const updatePortal = ( - hostName: string, - name: string, - children: React.ReactNode + hostName: string, + name: string, + children: React.ReactNode, ) => { - usePortal.setState((prev) => { - const next = new Map(prev.map); - const portal = next.get(hostName) ?? new Map(); - portal.set(name, children); - next.set(hostName, portal); - return { map: next }; - }); + usePortal.setState((prev) => { + const next = new Map(prev.map); + const portal = next.get(hostName) ?? new Map(); + portal.set(name, children); + next.set(hostName, portal); + return { map: next }; + }); }; const removePortal = (hostName: string, name: string) => { - usePortal.setState((prev) => { - const next = new Map(prev.map); - const portal = next.get(hostName) ?? new Map(); - portal.delete(name); - next.set(hostName, portal); - return { map: next }; - }); + usePortal.setState((prev) => { + const next = new Map(prev.map); + const portal = next.get(hostName) ?? new Map(); + portal.delete(name); + next.set(hostName, portal); + return { map: next }; + }); }; export function PortalHost({ name = DEFAULT_PORTAL_HOST }: { name?: string }) { - const portalMap = - usePortal((state) => state.map).get(name) ?? - new Map(); - if (portalMap.size === 0) return null; - return <>{Array.from(portalMap.values())}; + const portalMap = + usePortal((state) => state.map).get(name) ?? + new Map(); + if (portalMap.size === 0) return null; + return <>{Array.from(portalMap.values())}; } export function Portal({ - name, - hostName = DEFAULT_PORTAL_HOST, - children, + name, + hostName = DEFAULT_PORTAL_HOST, + children, }: { - name: string; - hostName?: string; - children: React.ReactNode; + name: string; + hostName?: string; + children: React.ReactNode; }) { - React.useEffect(() => { - updatePortal(hostName, name, children); - }, [hostName, name, children]); + React.useEffect(() => { + updatePortal(hostName, name, children); + }, [hostName, name, children]); - React.useEffect(() => { - return () => { - removePortal(hostName, name); - }; - }, [hostName, name]); + React.useEffect(() => { + return () => { + removePortal(hostName, name); + }; + }, [hostName, name]); - return null; + return null; } diff --git a/apps/native/src/components/primitives/radio-group/index.tsx b/apps/native/src/components/primitives/radio-group/index.tsx index 62d6182..47eeec6 100644 --- a/apps/native/src/components/primitives/radio-group/index.tsx +++ b/apps/native/src/components/primitives/radio-group/index.tsx @@ -1,116 +1,127 @@ -import * as React from 'react'; -import { Pressable, View, type GestureResponderEvent } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; +import * as React from "react"; +import { Pressable, View, type GestureResponderEvent } from "react-native"; +import * as Slot from "~/components/primitives/slot"; import type { - ForceMountable, - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; -import type { RadioGroupItemProps, RadioGroupRootProps } from './types'; + ForceMountable, + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; +import type { RadioGroupItemProps, RadioGroupRootProps } from "./types"; const RadioGroupContext = React.createContext(null); -const Root = React.forwardRef( - ({ asChild, value, onValueChange, disabled = false, ...viewProps }, ref) => { - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } -); +const Root = React.forwardRef< + ViewRef, + SlottableViewProps & RadioGroupRootProps +>(({ asChild, value, onValueChange, disabled = false, ...viewProps }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + + + ); +}); -Root.displayName = 'RootRadioGroup'; +Root.displayName = "RootRadioGroup"; function useRadioGroupContext() { - const context = React.useContext(RadioGroupContext); - if (!context) { - throw new Error( - 'RadioGroup compound components cannot be rendered outside the RadioGroup component' - ); - } - return context; + const context = React.useContext(RadioGroupContext); + if (!context) { + throw new Error( + "RadioGroup compound components cannot be rendered outside the RadioGroup component", + ); + } + return context; } interface RadioItemContext { - itemValue: string | undefined; + itemValue: string | undefined; } const RadioItemContext = React.createContext(null); -const Item = React.forwardRef( - ( - { asChild, value: itemValue, disabled: disabledProp = false, onPress: onPressProp, ...props }, - ref - ) => { - const { disabled, value, onValueChange } = useRadioGroupContext(); +const Item = React.forwardRef< + PressableRef, + SlottablePressableProps & RadioGroupItemProps +>( + ( + { + asChild, + value: itemValue, + disabled: disabledProp = false, + onPress: onPressProp, + ...props + }, + ref, + ) => { + const { disabled, value, onValueChange } = useRadioGroupContext(); - function onPress(ev: GestureResponderEvent) { - if (disabled || disabledProp) return; - onValueChange(itemValue); - onPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + if (disabled || disabledProp) return; + onValueChange(itemValue); + onPressProp?.(ev); + } - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -Item.displayName = 'ItemRadioGroup'; +Item.displayName = "ItemRadioGroup"; function useRadioItemContext() { - const context = React.useContext(RadioItemContext); - if (!context) { - throw new Error( - 'RadioItem compound components cannot be rendered outside the RadioItem component' - ); - } - return context; + const context = React.useContext(RadioItemContext); + if (!context) { + throw new Error( + "RadioItem compound components cannot be rendered outside the RadioItem component", + ); + } + return context; } -const Indicator = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { value } = useRadioGroupContext(); - const { itemValue } = useRadioItemContext(); +const Indicator = React.forwardRef< + ViewRef, + SlottableViewProps & ForceMountable +>(({ asChild, forceMount, ...props }, ref) => { + const { value } = useRadioGroupContext(); + const { itemValue } = useRadioItemContext(); - if (!forceMount) { - if (value !== itemValue) { - return null; - } - } - const Component = asChild ? Slot.View : View; - return ; - } -); + if (!forceMount) { + if (value !== itemValue) { + return null; + } + } + const Component = asChild ? Slot.View : View; + return ; +}); -Indicator.displayName = 'IndicatorRadioGroup'; +Indicator.displayName = "IndicatorRadioGroup"; export { Indicator, Item, Root }; diff --git a/apps/native/src/components/primitives/radio-group/types.ts b/apps/native/src/components/primitives/radio-group/types.ts index dd14ca3..3a5712c 100644 --- a/apps/native/src/components/primitives/radio-group/types.ts +++ b/apps/native/src/components/primitives/radio-group/types.ts @@ -1,15 +1,15 @@ interface RadioGroupRootProps { - value: string | undefined; - onValueChange: (val: string) => void; - disabled?: boolean; + value: string | undefined; + onValueChange: (val: string) => void; + disabled?: boolean; } interface RadioGroupItemProps { - value: string; - /** - * nativeID of the label element that describes this radio group item - */ - 'aria-labelledby': string; + value: string; + /** + * nativeID of the label element that describes this radio group item + */ + "aria-labelledby": string; } export type { RadioGroupRootProps, RadioGroupItemProps }; diff --git a/apps/native/src/components/primitives/select/index.tsx b/apps/native/src/components/primitives/select/index.tsx index 14d989d..6798035 100644 --- a/apps/native/src/components/primitives/select/index.tsx +++ b/apps/native/src/components/primitives/select/index.tsx @@ -1,455 +1,513 @@ -import * as React from 'react'; +import * as React from "react"; import { - BackHandler, - type GestureResponderEvent, - type LayoutChangeEvent, - type LayoutRectangle, - Pressable, - Text, - View, -} from 'react-native'; -import { type LayoutPosition, useControllableState, useRelativePosition } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; + BackHandler, + type GestureResponderEvent, + type LayoutChangeEvent, + type LayoutRectangle, + Pressable, + Text, + View, +} from "react-native"; +import { + type LayoutPosition, + useControllableState, + useRelativePosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - ForceMountable, - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableTextProps, - SlottableViewProps, - TextRef, - ViewRef, -} from '~/components/primitives/types'; + ForceMountable, + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableTextProps, + SlottableViewProps, + TextRef, + ViewRef, +} from "~/components/primitives/types"; import type { - RootContext as RootContextType, - SelectContentProps, - SelectItemProps, - SelectOverlayProps, - SelectPortalProps, - SelectRootProps, - SelectSeparatorProps, - SelectValueProps, -} from './types'; + RootContext as RootContextType, + SelectContentProps, + SelectItemProps, + SelectOverlayProps, + SelectPortalProps, + SelectRootProps, + SelectSeparatorProps, + SelectValueProps, +} from "./types"; interface IRootContext extends RootContextType { - triggerPosition: LayoutPosition | null; - setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; - contentLayout: LayoutRectangle | null; - setContentLayout: (contentLayout: LayoutRectangle | null) => void; - nativeID: string; + triggerPosition: LayoutPosition | null; + setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; + contentLayout: LayoutRectangle | null; + setContentLayout: (contentLayout: LayoutRectangle | null) => void; + nativeID: string; } const RootContext = React.createContext(null); const Root = React.forwardRef( - ( - { - asChild, - value: valueProp, - defaultValue, - onValueChange: onValueChangeProp, - open: openProp, - defaultOpen, - onOpenChange: onOpenChangeProp, - disabled, - ...viewProps - }, - ref - ) => { - const nativeID = React.useId(); - const [open = false, onOpenChange] = useControllableState({ - prop: openProp, - defaultProp: defaultOpen, - onChange: onOpenChangeProp, - }); - const [value, onValueChange] = useControllableState({ - prop: valueProp, - defaultProp: defaultValue, - onChange: onValueChangeProp, - }); - const [triggerPosition, setTriggerPosition] = React.useState(null); - const [contentLayout, setContentLayout] = React.useState(null); - - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ( + { + asChild, + value: valueProp, + defaultValue, + onValueChange: onValueChangeProp, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + disabled, + ...viewProps + }, + ref, + ) => { + const nativeID = React.useId(); + const [open = false, onOpenChange] = useControllableState({ + prop: openProp, + defaultProp: defaultOpen, + onChange: onOpenChangeProp, + }); + const [value, onValueChange] = useControllableState({ + prop: valueProp, + defaultProp: defaultValue, + onChange: onValueChangeProp, + }); + const [triggerPosition, setTriggerPosition] = + React.useState(null); + const [contentLayout, setContentLayout] = + React.useState(null); + + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeSelect'; +Root.displayName = "RootNativeSelect"; function useRootContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error('Select compound components cannot be rendered outside the Select component'); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "Select compound components cannot be rendered outside the Select component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const triggerRef = React.useRef(null); - const { open, onOpenChange, disabled: disabledRoot, setTriggerPosition } = useRootContext(); - - React.useImperativeHandle( - ref, - () => { - if (!triggerRef.current) { - return new View({}); - } - return triggerRef.current; - }, - [triggerRef.current] - ); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { - setTriggerPosition({ width, pageX, pageY: pageY, height }); - }); - onOpenChange(!open); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const triggerRef = React.useRef(null); + const { + open, + onOpenChange, + disabled: disabledRoot, + setTriggerPosition, + } = useRootContext(); + + React.useImperativeHandle( + ref, + () => { + if (!triggerRef.current) { + return new View({}); + } + return triggerRef.current; + }, + [triggerRef.current], + ); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { + setTriggerPosition({ width, pageX, pageY: pageY, height }); + }); + onOpenChange(!open); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeSelect'; +Trigger.displayName = "TriggerNativeSelect"; const Value = React.forwardRef( - ({ asChild, placeholder, ...props }, ref) => { - const { value } = useRootContext(); - const Component = asChild ? Slot.Text : Text; - return ( - - {value?.label ?? placeholder} - - ); - } + ({ asChild, placeholder, ...props }, ref) => { + const { value } = useRootContext(); + const Component = asChild ? Slot.Text : Text; + return ( + + {value?.label ?? placeholder} + + ); + }, ); -Value.displayName = 'ValueNativeSelect'; +Value.displayName = "ValueNativeSelect"; /** * @warning when using a custom ``, you might have to adjust the Content's sideOffset. */ function Portal({ forceMount, hostName, children }: SelectPortalProps) { - const value = useRootContext(); - - if (!value.triggerPosition) { - return null; - } - - if (!forceMount) { - if (!value.open) { - return null; - } - } - - return ( - - {children} - - ); + const value = useRootContext(); + + if (!value.triggerPosition) { + return null; + } + + if (!forceMount) { + if (!value.open) { + return null; + } + } + + return ( + + {children} + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => { - const { open, onOpenChange, setTriggerPosition, setContentLayout } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - OnPressProp?.(ev); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & SelectOverlayProps +>( + ( + { + asChild, + forceMount, + onPress: OnPressProp, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { open, onOpenChange, setTriggerPosition, setContentLayout } = + useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + OnPressProp?.(ev); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, ); -Overlay.displayName = 'OverlayNativeSelect'; +Overlay.displayName = "OverlayNativeSelect"; /** * @info `position`, `top`, `left`, and `maxWidth` style properties are controlled internally. Opt out of this behavior by setting `disablePositioningStyle` to `true`. */ const Content = React.forwardRef< - ViewRef, - SlottableViewProps & PositionedContentProps & SelectContentProps + ViewRef, + SlottableViewProps & PositionedContentProps & SelectContentProps >( - ( - { - asChild = false, - forceMount, - align = 'start', - side = 'bottom', - sideOffset = 0, - alignOffset = 0, - avoidCollisions = true, - onLayout: onLayoutProp, - insets, - style, - disablePositioningStyle, - position: _position, - ...props - }, - ref - ) => { - const { - open, - onOpenChange, - contentLayout, - nativeID, - triggerPosition, - setContentLayout, - setTriggerPosition, - } = useRootContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - return true; - }); - - return () => { - setContentLayout(null); - backHandler.remove(); - }; - }, []); - - const positionStyle = useRelativePosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, - }); - - function onLayout(event: LayoutChangeEvent) { - setContentLayout(event.nativeEvent.layout); - onLayoutProp?.(event); - } - - if (!forceMount) { - if (!open) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } + ( + { + asChild = false, + forceMount, + align = "start", + side = "bottom", + sideOffset = 0, + alignOffset = 0, + avoidCollisions = true, + onLayout: onLayoutProp, + insets, + style, + disablePositioningStyle, + position: _position, + ...props + }, + ref, + ) => { + const { + open, + onOpenChange, + contentLayout, + nativeID, + triggerPosition, + setContentLayout, + setTriggerPosition, + } = useRootContext(); + + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + return true; + }, + ); + + return () => { + setContentLayout(null); + backHandler.remove(); + }; + }, []); + + const positionStyle = useRelativePosition({ + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, + }); + + function onLayout(event: LayoutChangeEvent) { + setContentLayout(event.nativeEvent.layout); + onLayoutProp?.(event); + } + + if (!forceMount) { + if (!open) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); + }, ); -Content.displayName = 'ContentNativeSelect'; +Content.displayName = "ContentNativeSelect"; const ItemContext = React.createContext<{ - itemValue: string; - label: string; + itemValue: string; + label: string; } | null>(null); -const Item = React.forwardRef( - ( - { - asChild, - value: itemValue, - label, - onPress: onPressProp, - disabled = false, - closeOnPress = true, - ...props - }, - ref - ) => { - const { onOpenChange, value, onValueChange, setTriggerPosition, setContentLayout } = - useRootContext(); - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - - onValueChange({ value: itemValue, label }); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } +const Item = React.forwardRef< + PressableRef, + SlottablePressableProps & SelectItemProps +>( + ( + { + asChild, + value: itemValue, + label, + onPress: onPressProp, + disabled = false, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { + onOpenChange, + value, + onValueChange, + setTriggerPosition, + setContentLayout, + } = useRootContext(); + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + + onValueChange({ value: itemValue, label }); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -Item.displayName = 'ItemNativeSelect'; +Item.displayName = "ItemNativeSelect"; function useItemContext() { - const context = React.useContext(ItemContext); - if (!context) { - throw new Error('Item compound components cannot be rendered outside of an Item component'); - } - return context; + const context = React.useContext(ItemContext); + if (!context) { + throw new Error( + "Item compound components cannot be rendered outside of an Item component", + ); + } + return context; } -const ItemText = React.forwardRef>( - ({ asChild, ...props }, ref) => { - const { label } = useItemContext(); - - const Component = asChild ? Slot.Text : Text; - return ( - - {label} - - ); - } -); - -ItemText.displayName = 'ItemTextNativeSelect'; - -const ItemIndicator = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { itemValue } = useItemContext(); - const { value } = useRootContext(); - - if (!forceMount) { - if (value?.value !== itemValue) { - return null; - } - } - const Component = asChild ? Slot.View : View; - return ; - } -); - -ItemIndicator.displayName = 'ItemIndicatorNativeSelect'; +const ItemText = React.forwardRef< + TextRef, + Omit +>(({ asChild, ...props }, ref) => { + const { label } = useItemContext(); + + const Component = asChild ? Slot.Text : Text; + return ( + + {label} + + ); +}); -const Group = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; +ItemText.displayName = "ItemTextNativeSelect"; + +const ItemIndicator = React.forwardRef< + ViewRef, + SlottableViewProps & ForceMountable +>(({ asChild, forceMount, ...props }, ref) => { + const { itemValue } = useItemContext(); + const { value } = useRootContext(); + + if (!forceMount) { + if (value?.value !== itemValue) { + return null; + } + } + const Component = asChild ? Slot.View : View; + return ; }); -Group.displayName = 'GroupNativeSelect'; +ItemIndicator.displayName = "ItemIndicatorNativeSelect"; -const Label = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Text : Text; - return ; -}); +const Group = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); -Label.displayName = 'LabelNativeSelect'; +Group.displayName = "GroupNativeSelect"; -const Separator = React.forwardRef( - ({ asChild, decorative, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; - } +const Label = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Text : Text; + return ; + }, ); -Separator.displayName = 'SeparatorNativeSelect'; +Label.displayName = "LabelNativeSelect"; + +const Separator = React.forwardRef< + ViewRef, + SlottableViewProps & SelectSeparatorProps +>(({ asChild, decorative, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + ); +}); + +Separator.displayName = "SeparatorNativeSelect"; -const ScrollUpButton = ({ children }: { children?: React.ReactNode; className?: string }) => { - return children; +const ScrollUpButton = ({ + children, +}: { children?: React.ReactNode; className?: string }) => { + return children; }; -const ScrollDownButton = ({ children }: { children?: React.ReactNode; className?: string }) => { - return children; +const ScrollDownButton = ({ + children, +}: { children?: React.ReactNode; className?: string }) => { + return children; }; -const Viewport = ({ children }: { children?: React.ReactNode; className?: string }) => { - return children; +const Viewport = ({ + children, +}: { children?: React.ReactNode; className?: string }) => { + return children; }; export { - Content, - Group, - Item, - ItemIndicator, - ItemText, - Label, - Overlay, - Portal, - Root, - ScrollDownButton, - ScrollUpButton, - Separator, - Trigger, - Value, - Viewport, - useItemContext, - useRootContext, + Content, + Group, + Item, + ItemIndicator, + ItemText, + Label, + Overlay, + Portal, + Root, + ScrollDownButton, + ScrollUpButton, + Separator, + Trigger, + Value, + Viewport, + useItemContext, + useRootContext, }; -export type { Option } from './types'; +export type { Option } from "./types"; function onStartShouldSetResponder() { - return true; + return true; } diff --git a/apps/native/src/components/primitives/select/types.ts b/apps/native/src/components/primitives/select/types.ts index 2787f96..a9d5930 100644 --- a/apps/native/src/components/primitives/select/types.ts +++ b/apps/native/src/components/primitives/select/types.ts @@ -1,87 +1,87 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; type Option = - | { - value: string; - label: string; - } - | undefined; + | { + value: string; + label: string; + } + | undefined; interface RootContext { - value: Option; - onValueChange: (option: Option) => void; - open: boolean; - onOpenChange: (value: boolean) => void; - disabled?: boolean; + value: Option; + onValueChange: (option: Option) => void; + open: boolean; + onOpenChange: (value: boolean) => void; + disabled?: boolean; } interface SelectRootProps { - value?: Option; - defaultValue?: Option; - onValueChange?: (option: Option) => void; - open?: boolean; - defaultOpen?: boolean; - onOpenChange?: (value: boolean) => void; - disabled?: boolean; - /** - * Platform: WEB ONLY - */ - dir?: 'ltr' | 'rtl'; - /** - * Platform: WEB ONLY - */ - name?: string; - /** - * Platform: WEB ONLY - */ - required?: boolean; + value?: Option; + defaultValue?: Option; + onValueChange?: (option: Option) => void; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (value: boolean) => void; + disabled?: boolean; + /** + * Platform: WEB ONLY + */ + dir?: "ltr" | "rtl"; + /** + * Platform: WEB ONLY + */ + name?: string; + /** + * Platform: WEB ONLY + */ + required?: boolean; } interface SelectValueProps { - placeholder: string; + placeholder: string; } interface SelectPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } interface SelectOverlayProps extends ForceMountable { - closeOnPress?: boolean; + closeOnPress?: boolean; } interface SelectContentProps { - /** - * Platform: WEB ONLY - */ - position?: 'popper' | 'item-aligned' | undefined; + /** + * Platform: WEB ONLY + */ + position?: "popper" | "item-aligned" | undefined; } interface SelectItemProps { - value: string; - label: string; - closeOnPress?: boolean; + value: string; + label: string; + closeOnPress?: boolean; } interface SelectSeparatorProps { - decorative?: boolean; + decorative?: boolean; } export type { - Option, - RootContext, - SelectContentProps, - SelectItemProps, - SelectOverlayProps, - SelectPortalProps, - SelectRootProps, - SelectSeparatorProps, - SelectValueProps, + Option, + RootContext, + SelectContentProps, + SelectItemProps, + SelectOverlayProps, + SelectPortalProps, + SelectRootProps, + SelectSeparatorProps, + SelectValueProps, }; diff --git a/apps/native/src/components/primitives/separator/index.tsx b/apps/native/src/components/primitives/separator/index.tsx index f21c733..25b62a7 100644 --- a/apps/native/src/components/primitives/separator/index.tsx +++ b/apps/native/src/components/primitives/separator/index.tsx @@ -1,23 +1,26 @@ -import * as React from 'react'; -import { View } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; -import type { SlottableViewProps, ViewRef } from '~/components/primitives/types'; -import type { SeparatorRootProps } from './types'; +import * as React from "react"; +import { View } from "react-native"; +import * as Slot from "~/components/primitives/slot"; +import type { + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; +import type { SeparatorRootProps } from "./types"; const Root = React.forwardRef( - ({ asChild, decorative, orientation = 'horizontal', ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ( - - ); - } + ({ asChild, decorative, orientation = "horizontal", ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + ); + }, ); -Root.displayName = 'RootSeparator'; +Root.displayName = "RootSeparator"; export { Root }; diff --git a/apps/native/src/components/primitives/separator/types.ts b/apps/native/src/components/primitives/separator/types.ts index fd6434a..a1e0aa9 100644 --- a/apps/native/src/components/primitives/separator/types.ts +++ b/apps/native/src/components/primitives/separator/types.ts @@ -1,6 +1,6 @@ interface SeparatorRootProps { - orientation?: 'horizontal' | 'vertical'; - decorative?: boolean; + orientation?: "horizontal" | "vertical"; + decorative?: boolean; } export type { SeparatorRootProps }; diff --git a/apps/native/src/components/primitives/slot.tsx b/apps/native/src/components/primitives/slot.tsx index 403b91e..cc683e7 100644 --- a/apps/native/src/components/primitives/slot.tsx +++ b/apps/native/src/components/primitives/slot.tsx @@ -1,106 +1,116 @@ -import * as React from 'react'; +import * as React from "react"; import { - Image as RNImage, - Pressable as RNPressable, - Text as RNText, - View as RNView, - StyleSheet, - type PressableStateCallbackType, - type ImageProps as RNImageProps, - type ImageStyle as RNImageStyle, - type PressableProps as RNPressableProps, - type TextProps as RNTextProps, - type ViewProps as RNViewProps, - type StyleProp, -} from 'react-native'; - -const Pressable = React.forwardRef, RNPressableProps>( - (props, forwardedRef) => { - const { children, ...pressableSlotProps } = props; - - if (!React.isValidElement(children)) { - console.log('Slot.Pressable - Invalid asChild element', children); - return null; - } - - return React.cloneElement< - React.ComponentPropsWithoutRef, - React.ElementRef - >(isTextChildren(children) ? <> : children, { - ...mergeProps(pressableSlotProps, children.props), - ref: forwardedRef ? composeRefs(forwardedRef, (children as any).ref) : (children as any).ref, - }); - } -); - -Pressable.displayName = 'SlotPressable'; + Image as RNImage, + Pressable as RNPressable, + Text as RNText, + View as RNView, + StyleSheet, + type PressableStateCallbackType, + type ImageProps as RNImageProps, + type ImageStyle as RNImageStyle, + type PressableProps as RNPressableProps, + type TextProps as RNTextProps, + type ViewProps as RNViewProps, + type StyleProp, +} from "react-native"; + +const Pressable = React.forwardRef< + React.ElementRef, + RNPressableProps +>((props, forwardedRef) => { + const { children, ...pressableSlotProps } = props; + + if (!React.isValidElement(children)) { + console.log("Slot.Pressable - Invalid asChild element", children); + return null; + } + + return React.cloneElement< + React.ComponentPropsWithoutRef, + React.ElementRef + >(isTextChildren(children) ? <> : children, { + ...mergeProps(pressableSlotProps, children.props), + ref: forwardedRef + ? composeRefs(forwardedRef, (children as any).ref) + : (children as any).ref, + }); +}); + +Pressable.displayName = "SlotPressable"; const View = React.forwardRef, RNViewProps>( - (props, forwardedRef) => { - const { children, ...viewSlotProps } = props; - - if (!React.isValidElement(children)) { - console.log('Slot.View - Invalid asChild element', children); - return null; - } - - return React.cloneElement< - React.ComponentPropsWithoutRef, - React.ElementRef - >(isTextChildren(children) ? <> : children, { - ...mergeProps(viewSlotProps, children.props), - ref: forwardedRef ? composeRefs(forwardedRef, (children as any).ref) : (children as any).ref, - }); - } + (props, forwardedRef) => { + const { children, ...viewSlotProps } = props; + + if (!React.isValidElement(children)) { + console.log("Slot.View - Invalid asChild element", children); + return null; + } + + return React.cloneElement< + React.ComponentPropsWithoutRef, + React.ElementRef + >(isTextChildren(children) ? <> : children, { + ...mergeProps(viewSlotProps, children.props), + ref: forwardedRef + ? composeRefs(forwardedRef, (children as any).ref) + : (children as any).ref, + }); + }, ); -View.displayName = 'SlotView'; +View.displayName = "SlotView"; const Text = React.forwardRef, RNTextProps>( - (props, forwardedRef) => { - const { children, ...textSlotProps } = props; - - if (!React.isValidElement(children)) { - console.log('Slot.Text - Invalid asChild element', children); - return null; - } - - return React.cloneElement< - React.ComponentPropsWithoutRef, - React.ElementRef - >(isTextChildren(children) ? <> : children, { - ...mergeProps(textSlotProps, children.props), - ref: forwardedRef ? composeRefs(forwardedRef, (children as any).ref) : (children as any).ref, - }); - } + (props, forwardedRef) => { + const { children, ...textSlotProps } = props; + + if (!React.isValidElement(children)) { + console.log("Slot.Text - Invalid asChild element", children); + return null; + } + + return React.cloneElement< + React.ComponentPropsWithoutRef, + React.ElementRef + >(isTextChildren(children) ? <> : children, { + ...mergeProps(textSlotProps, children.props), + ref: forwardedRef + ? composeRefs(forwardedRef, (children as any).ref) + : (children as any).ref, + }); + }, ); -Text.displayName = 'SlotText'; +Text.displayName = "SlotText"; type ImageSlotProps = RNImageProps & { - children?: React.ReactNode; + children?: React.ReactNode; }; -const Image = React.forwardRef, ImageSlotProps>( - (props, forwardedRef) => { - const { children, ...imageSlotProps } = props; - - if (!React.isValidElement(children)) { - console.log('Slot.Image - Invalid asChild element', children); - return null; - } - - return React.cloneElement< - React.ComponentPropsWithoutRef, - React.ElementRef - >(isTextChildren(children) ? <> : children, { - ...mergeProps(imageSlotProps, children.props), - ref: forwardedRef ? composeRefs(forwardedRef, (children as any).ref) : (children as any).ref, - }); - } -); - -Image.displayName = 'SlotImage'; +const Image = React.forwardRef< + React.ElementRef, + ImageSlotProps +>((props, forwardedRef) => { + const { children, ...imageSlotProps } = props; + + if (!React.isValidElement(children)) { + console.log("Slot.Image - Invalid asChild element", children); + return null; + } + + return React.cloneElement< + React.ComponentPropsWithoutRef, + React.ElementRef + >(isTextChildren(children) ? <> : children, { + ...mergeProps(imageSlotProps, children.props), + ref: forwardedRef + ? composeRefs(forwardedRef, (children as any).ref) + : (children as any).ref, + }); +}); + +Image.displayName = "SlotImage"; export { Image, Pressable, Text, View }; @@ -109,79 +119,87 @@ export { Image, Pressable, Text, View }; // https://github.com/radix-ui/primitives/tree/main function composeRefs(...refs: (React.Ref | undefined)[]) { - return (node: T) => - refs.forEach((ref) => { - if (typeof ref === 'function') { - ref(node); - } else if (ref != null) { - (ref as React.MutableRefObject).current = node; - } - }); + return (node: T) => + refs.forEach((ref) => { + if (typeof ref === "function") { + ref(node); + } else if (ref != null) { + (ref as React.MutableRefObject).current = node; + } + }); } type AnyProps = Record; function mergeProps(slotProps: AnyProps, childProps: AnyProps) { - // all child props should override - const overrideProps = { ...childProps }; - - for (const propName in childProps) { - const slotPropValue = slotProps[propName]; - const childPropValue = childProps[propName]; - - const isHandler = /^on[A-Z]/.test(propName); - if (isHandler) { - // if the handler exists on both, we compose them - if (slotPropValue && childPropValue) { - overrideProps[propName] = (...args: unknown[]) => { - childPropValue(...args); - slotPropValue(...args); - }; - } - // but if it exists only on the slot, we use only this one - else if (slotPropValue) { - overrideProps[propName] = slotPropValue; - } - } - // if it's `style`, we merge them - else if (propName === 'style') { - overrideProps[propName] = combineStyles(slotPropValue, childPropValue); - } else if (propName === 'className') { - overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(' '); - } - } - - return { ...slotProps, ...overrideProps }; + // all child props should override + const overrideProps = { ...childProps }; + + for (const propName in childProps) { + const slotPropValue = slotProps[propName]; + const childPropValue = childProps[propName]; + + const isHandler = /^on[A-Z]/.test(propName); + if (isHandler) { + // if the handler exists on both, we compose them + if (slotPropValue && childPropValue) { + overrideProps[propName] = (...args: unknown[]) => { + childPropValue(...args); + slotPropValue(...args); + }; + } + // but if it exists only on the slot, we use only this one + else if (slotPropValue) { + overrideProps[propName] = slotPropValue; + } + } + // if it's `style`, we merge them + else if (propName === "style") { + overrideProps[propName] = combineStyles(slotPropValue, childPropValue); + } else if (propName === "className") { + overrideProps[propName] = [slotPropValue, childPropValue] + .filter(Boolean) + .join(" "); + } + } + + return { ...slotProps, ...overrideProps }; } -type PressableStyle = RNPressableProps['style']; +type PressableStyle = RNPressableProps["style"]; type ImageStyle = StyleProp; type Style = PressableStyle | ImageStyle; function combineStyles(slotStyle?: Style, childValue?: Style) { - if (typeof slotStyle === 'function' && typeof childValue === 'function') { - return (state: PressableStateCallbackType) => { - return StyleSheet.flatten([slotStyle(state), childValue(state)]); - }; - } - if (typeof slotStyle === 'function') { - return (state: PressableStateCallbackType) => { - return childValue ? StyleSheet.flatten([slotStyle(state), childValue]) : slotStyle(state); - }; - } - if (typeof childValue === 'function') { - return (state: PressableStateCallbackType) => { - return slotStyle ? StyleSheet.flatten([slotStyle, childValue(state)]) : childValue(state); - }; - } - - return StyleSheet.flatten([slotStyle, childValue].filter(Boolean)); + if (typeof slotStyle === "function" && typeof childValue === "function") { + return (state: PressableStateCallbackType) => { + return StyleSheet.flatten([slotStyle(state), childValue(state)]); + }; + } + if (typeof slotStyle === "function") { + return (state: PressableStateCallbackType) => { + return childValue + ? StyleSheet.flatten([slotStyle(state), childValue]) + : slotStyle(state); + }; + } + if (typeof childValue === "function") { + return (state: PressableStateCallbackType) => { + return slotStyle + ? StyleSheet.flatten([slotStyle, childValue(state)]) + : childValue(state); + }; + } + + return StyleSheet.flatten([slotStyle, childValue].filter(Boolean)); } export function isTextChildren( - children: React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode) + children: + | React.ReactNode + | ((state: PressableStateCallbackType) => React.ReactNode), ) { - return Array.isArray(children) - ? children.every((child) => typeof child === 'string') - : typeof children === 'string'; + return Array.isArray(children) + ? children.every((child) => typeof child === "string") + : typeof children === "string"; } diff --git a/apps/native/src/components/primitives/switch/index.tsx b/apps/native/src/components/primitives/switch/index.tsx index ceebe36..9c5c0a2 100644 --- a/apps/native/src/components/primitives/switch/index.tsx +++ b/apps/native/src/components/primitives/switch/index.tsx @@ -1,65 +1,65 @@ -import * as React from 'react'; -import { Pressable, View, type GestureResponderEvent } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; +import * as React from "react"; +import { Pressable, View, type GestureResponderEvent } from "react-native"; +import * as Slot from "~/components/primitives/slot"; import type { - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; -import type { SwitchRootProps } from './types'; + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; +import type { SwitchRootProps } from "./types"; const Root = React.forwardRef< - PressableRef, - SlottablePressableProps & SwitchRootProps + PressableRef, + SlottablePressableProps & SwitchRootProps >( - ( - { - asChild, - checked, - onCheckedChange, - disabled, - onPress: onPressProp, - 'aria-valuetext': ariaValueText, - ...props - }, - ref - ) => { - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - onCheckedChange(!checked); - onPressProp?.(ev); - } + ( + { + asChild, + checked, + onCheckedChange, + disabled, + onPress: onPressProp, + "aria-valuetext": ariaValueText, + ...props + }, + ref, + ) => { + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + onCheckedChange(!checked); + onPressProp?.(ev); + } - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Root.displayName = 'RootNativeSwitch'; +Root.displayName = "RootNativeSwitch"; const Thumb = React.forwardRef( - ({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; - } + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, ); -Thumb.displayName = 'ThumbNativeSwitch'; +Thumb.displayName = "ThumbNativeSwitch"; export { Root, Thumb }; diff --git a/apps/native/src/components/primitives/switch/types.ts b/apps/native/src/components/primitives/switch/types.ts index 986c204..48069ef 100644 --- a/apps/native/src/components/primitives/switch/types.ts +++ b/apps/native/src/components/primitives/switch/types.ts @@ -1,11 +1,11 @@ interface SwitchRootProps { - checked: boolean; - onCheckedChange: (checked: boolean) => void; - disabled?: boolean; - /** - * Platform: WEB ONLY - */ - onKeyDown?: (ev: React.KeyboardEvent) => void; + checked: boolean; + onCheckedChange: (checked: boolean) => void; + disabled?: boolean; + /** + * Platform: WEB ONLY + */ + onKeyDown?: (ev: React.KeyboardEvent) => void; } export type { SwitchRootProps }; diff --git a/apps/native/src/components/primitives/table.tsx b/apps/native/src/components/primitives/table.tsx index 509b687..cfe3645 100644 --- a/apps/native/src/components/primitives/table.tsx +++ b/apps/native/src/components/primitives/table.tsx @@ -1,55 +1,67 @@ -import * as React from 'react'; -import { Pressable, View } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; +import * as React from "react"; +import { Pressable, View } from "react-native"; +import * as Slot from "~/components/primitives/slot"; import type { - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; - -const Root = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); -Root.displayName = 'RootTable'; - -const Header = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); -Header.displayName = 'HeaderTable'; + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; + +const Root = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); +Root.displayName = "RootTable"; + +const Header = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); +Header.displayName = "HeaderTable"; const Row = React.forwardRef( - ({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, +); +Row.displayName = "RowTable"; + +const Head = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); +Head.displayName = "HeadTable"; + +const Body = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); +Body.displayName = "BodyTable"; + +const Cell = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); +Cell.displayName = "CellTable"; + +const Footer = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, ); -Row.displayName = 'RowTable'; - -const Head = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); -Head.displayName = 'HeadTable'; - -const Body = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); -Body.displayName = 'BodyTable'; - -const Cell = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); -Cell.displayName = 'CellTable'; - -const Footer = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); -Footer.displayName = 'FooterTable'; +Footer.displayName = "FooterTable"; export { Body, Cell, Footer, Head, Header, Root, Row }; diff --git a/apps/native/src/components/primitives/tabs/index.tsx b/apps/native/src/components/primitives/tabs/index.tsx index e9fd2df..f719d99 100644 --- a/apps/native/src/components/primitives/tabs/index.tsx +++ b/apps/native/src/components/primitives/tabs/index.tsx @@ -1,133 +1,147 @@ -import * as React from 'react'; -import { Pressable, View, type GestureResponderEvent } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; -import type { ComponentPropsWithAsChild, SlottableViewProps, ViewRef } from '~/components/primitives/types'; -import type { TabsContentProps, TabsRootProps } from './types'; +import * as React from "react"; +import { Pressable, View, type GestureResponderEvent } from "react-native"; +import * as Slot from "~/components/primitives/slot"; +import type { + ComponentPropsWithAsChild, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; +import type { TabsContentProps, TabsRootProps } from "./types"; interface RootContext extends TabsRootProps { - nativeID: string; + nativeID: string; } const TabsContext = React.createContext(null); const Root = React.forwardRef( - ( - { - asChild, - value, - onValueChange, - orientation: _orientation, - dir: _dir, - activationMode: _activationMode, - ...viewProps - }, - ref - ) => { - const nativeID = React.useId(); - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + ( + { + asChild, + value, + onValueChange, + orientation: _orientation, + dir: _dir, + activationMode: _activationMode, + ...viewProps + }, + ref, + ) => { + const nativeID = React.useId(); + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeTabs'; +Root.displayName = "RootNativeTabs"; function useRootContext() { - const context = React.useContext(TabsContext); - if (!context) { - throw new Error('Tabs compound components cannot be rendered outside the Tabs component'); - } - return context; + const context = React.useContext(TabsContext); + if (!context) { + throw new Error( + "Tabs compound components cannot be rendered outside the Tabs component", + ); + } + return context; } -const List = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; -}); +const List = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.View : View; + return ; + }, +); -List.displayName = 'ListNativeTabs'; +List.displayName = "ListNativeTabs"; const TriggerContext = React.createContext<{ value: string } | null>(null); const Trigger = React.forwardRef< - React.ElementRef, - ComponentPropsWithAsChild & { - value: string; - } ->(({ asChild, onPress: onPressProp, disabled, value: tabValue, ...props }, ref) => { - const { onValueChange, value: rootValue, nativeID } = useRootContext(); - - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - onValueChange(tabValue); - onPressProp?.(ev); - } - - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); -}); + React.ElementRef, + ComponentPropsWithAsChild & { + value: string; + } +>( + ( + { asChild, onPress: onPressProp, disabled, value: tabValue, ...props }, + ref, + ) => { + const { onValueChange, value: rootValue, nativeID } = useRootContext(); + + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + onValueChange(tabValue); + onPressProp?.(ev); + } + + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, +); -Trigger.displayName = 'TriggerNativeTabs'; +Trigger.displayName = "TriggerNativeTabs"; function useTriggerContext() { - const context = React.useContext(TriggerContext); - if (!context) { - throw new Error( - 'Tabs.Trigger compound components cannot be rendered outside the Tabs.Trigger component' - ); - } - return context; + const context = React.useContext(TriggerContext); + if (!context) { + throw new Error( + "Tabs.Trigger compound components cannot be rendered outside the Tabs.Trigger component", + ); + } + return context; } -const Content = React.forwardRef( - ({ asChild, forceMount, value: tabValue, ...props }, ref) => { - const { value: rootValue, nativeID } = useRootContext(); - - if (!forceMount) { - if (rootValue !== tabValue) { - return null; - } - } - - const Component = asChild ? Slot.View : View; - return ( - - ); - } -); +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & TabsContentProps +>(({ asChild, forceMount, value: tabValue, ...props }, ref) => { + const { value: rootValue, nativeID } = useRootContext(); + + if (!forceMount) { + if (rootValue !== tabValue) { + return null; + } + } + + const Component = asChild ? Slot.View : View; + return ( + + ); +}); -Content.displayName = 'ContentNativeTabs'; +Content.displayName = "ContentNativeTabs"; export { Content, List, Root, Trigger, useRootContext, useTriggerContext }; diff --git a/apps/native/src/components/primitives/tabs/types.ts b/apps/native/src/components/primitives/tabs/types.ts index d78a066..db014b8 100644 --- a/apps/native/src/components/primitives/tabs/types.ts +++ b/apps/native/src/components/primitives/tabs/types.ts @@ -1,24 +1,24 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface TabsRootProps { - value: string; - onValueChange: (value: string) => void; - /** - * Platform: WEB ONLY - */ - orientation?: 'horizontal' | 'vertical'; - /** - * Platform: WEB ONLY - */ - dir?: 'ltr' | 'rtl'; - /** - * Platform: WEB ONLY - */ - activationMode?: 'automatic' | 'manual'; + value: string; + onValueChange: (value: string) => void; + /** + * Platform: WEB ONLY + */ + orientation?: "horizontal" | "vertical"; + /** + * Platform: WEB ONLY + */ + dir?: "ltr" | "rtl"; + /** + * Platform: WEB ONLY + */ + activationMode?: "automatic" | "manual"; } interface TabsContentProps extends ForceMountable { - value: string; + value: string; } export type { TabsContentProps, TabsRootProps }; diff --git a/apps/native/src/components/primitives/toggle-group/index.tsx b/apps/native/src/components/primitives/toggle-group/index.tsx index d799fc0..d66746b 100644 --- a/apps/native/src/components/primitives/toggle-group/index.tsx +++ b/apps/native/src/components/primitives/toggle-group/index.tsx @@ -1,123 +1,141 @@ -import * as React from 'react'; -import { Pressable, View, type GestureResponderEvent } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; +import * as React from "react"; +import { Pressable, View, type GestureResponderEvent } from "react-native"; +import * as Slot from "~/components/primitives/slot"; import type { - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; -import { ToggleGroupUtils } from '~/components/primitives/utils'; -import type { ToggleGroupItemProps, ToggleGroupRootProps } from './types'; + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; +import { ToggleGroupUtils } from "~/components/primitives/utils"; +import type { ToggleGroupItemProps, ToggleGroupRootProps } from "./types"; -const ToggleGroupContext = React.createContext(null); +const ToggleGroupContext = React.createContext( + null, +); -const Root = React.forwardRef( - ( - { - asChild, - type, - value, - onValueChange, - disabled = false, - rovingFocus: _rovingFocus, - orientation: _orientation, - dir: _dir, - loop: _loop, - ...viewProps - }, - ref - ) => { - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } +const Root = React.forwardRef< + ViewRef, + SlottableViewProps & ToggleGroupRootProps +>( + ( + { + asChild, + type, + value, + onValueChange, + disabled = false, + rovingFocus: _rovingFocus, + orientation: _orientation, + dir: _dir, + loop: _loop, + ...viewProps + }, + ref, + ) => { + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootToggleGroup'; +Root.displayName = "RootToggleGroup"; function useRootContext() { - const context = React.useContext(ToggleGroupContext); - if (!context) { - throw new Error( - 'ToggleGroup compound components cannot be rendered outside the ToggleGroup component' - ); - } - return context; + const context = React.useContext(ToggleGroupContext); + if (!context) { + throw new Error( + "ToggleGroup compound components cannot be rendered outside the ToggleGroup component", + ); + } + return context; } const ItemContext = React.createContext(null); -const Item = React.forwardRef( - ( - { asChild, value: itemValue, disabled: disabledProp = false, onPress: onPressProp, ...props }, - ref - ) => { - const id = React.useId(); - const { type, disabled, value, onValueChange } = useRootContext(); +const Item = React.forwardRef< + PressableRef, + SlottablePressableProps & ToggleGroupItemProps +>( + ( + { + asChild, + value: itemValue, + disabled: disabledProp = false, + onPress: onPressProp, + ...props + }, + ref, + ) => { + const id = React.useId(); + const { type, disabled, value, onValueChange } = useRootContext(); - function onPress(ev: GestureResponderEvent) { - if (disabled || disabledProp) return; - if (type === 'single') { - onValueChange(ToggleGroupUtils.getNewSingleValue(value, itemValue)); - } - if (type === 'multiple') { - onValueChange(ToggleGroupUtils.getNewMultipleValue(value, itemValue)); - } - onPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + if (disabled || disabledProp) return; + if (type === "single") { + onValueChange(ToggleGroupUtils.getNewSingleValue(value, itemValue)); + } + if (type === "multiple") { + onValueChange(ToggleGroupUtils.getNewMultipleValue(value, itemValue)); + } + onPressProp?.(ev); + } - const isChecked = - type === 'single' ? ToggleGroupUtils.getIsSelected(value, itemValue) : undefined; - const isSelected = - type === 'multiple' ? ToggleGroupUtils.getIsSelected(value, itemValue) : undefined; + const isChecked = + type === "single" + ? ToggleGroupUtils.getIsSelected(value, itemValue) + : undefined; + const isSelected = + type === "multiple" + ? ToggleGroupUtils.getIsSelected(value, itemValue) + : undefined; - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); + }, ); -Item.displayName = 'ItemToggleGroup'; +Item.displayName = "ItemToggleGroup"; function useItemContext() { - const context = React.useContext(ItemContext); - if (!context) { - throw new Error( - 'ToggleGroupItem compound components cannot be rendered outside the ToggleGroupItem component' - ); - } - return context; + const context = React.useContext(ItemContext); + if (!context) { + throw new Error( + "ToggleGroupItem compound components cannot be rendered outside the ToggleGroupItem component", + ); + } + return context; } const utils = ToggleGroupUtils; diff --git a/apps/native/src/components/primitives/toggle-group/types.ts b/apps/native/src/components/primitives/toggle-group/types.ts index da7e64e..c22d61c 100644 --- a/apps/native/src/components/primitives/toggle-group/types.ts +++ b/apps/native/src/components/primitives/toggle-group/types.ts @@ -1,37 +1,37 @@ type SingleRootProps = { - type: 'single'; - value: string | undefined; - onValueChange: (val: string | undefined) => void; + type: "single"; + value: string | undefined; + onValueChange: (val: string | undefined) => void; }; type MultipleRootProps = { - type: 'multiple'; - value: string[]; - onValueChange: (val: string[]) => void; + type: "multiple"; + value: string[]; + onValueChange: (val: string[]) => void; }; type ToggleGroupRootProps = (SingleRootProps | MultipleRootProps) & { - disabled?: boolean; - /** - * Platform: WEB ONLY - */ - rovingFocus?: boolean; - /** - * Platform: WEB ONLY - */ - orientation?: 'horizontal' | 'vertical'; - /** - * Platform: WEB ONLY - */ - dir?: 'ltr' | 'rtl'; - /** - * Platform: WEB ONLY - */ - loop?: boolean; + disabled?: boolean; + /** + * Platform: WEB ONLY + */ + rovingFocus?: boolean; + /** + * Platform: WEB ONLY + */ + orientation?: "horizontal" | "vertical"; + /** + * Platform: WEB ONLY + */ + dir?: "ltr" | "rtl"; + /** + * Platform: WEB ONLY + */ + loop?: boolean; }; interface ToggleGroupItemProps { - value: string; + value: string; } export type { ToggleGroupRootProps, ToggleGroupItemProps }; diff --git a/apps/native/src/components/primitives/toggle/index.tsx b/apps/native/src/components/primitives/toggle/index.tsx index d124ba3..1b1d6ff 100644 --- a/apps/native/src/components/primitives/toggle/index.tsx +++ b/apps/native/src/components/primitives/toggle/index.tsx @@ -1,37 +1,53 @@ -import * as React from 'react'; -import { Pressable, type GestureResponderEvent } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; -import type { PressableRef, SlottablePressableProps } from '~/components/primitives/types'; -import type { ToggleRootProps } from './types'; +import * as React from "react"; +import { Pressable, type GestureResponderEvent } from "react-native"; +import * as Slot from "~/components/primitives/slot"; +import type { + PressableRef, + SlottablePressableProps, +} from "~/components/primitives/types"; +import type { ToggleRootProps } from "./types"; -const Root = React.forwardRef( - ({ asChild, pressed, onPressedChange, disabled, onPress: onPressProp, ...props }, ref) => { - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - const newValue = !pressed; - onPressedChange(newValue); - onPressProp?.(ev); - } +const Root = React.forwardRef< + PressableRef, + SlottablePressableProps & ToggleRootProps +>( + ( + { + asChild, + pressed, + onPressedChange, + disabled, + onPress: onPressProp, + ...props + }, + ref, + ) => { + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + const newValue = !pressed; + onPressedChange(newValue); + onPressProp?.(ev); + } - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Root.displayName = 'RootNativeToggle'; +Root.displayName = "RootNativeToggle"; export { Root }; diff --git a/apps/native/src/components/primitives/toggle/types.ts b/apps/native/src/components/primitives/toggle/types.ts index 3ccc248..aa42280 100644 --- a/apps/native/src/components/primitives/toggle/types.ts +++ b/apps/native/src/components/primitives/toggle/types.ts @@ -1,7 +1,7 @@ interface ToggleRootProps { - pressed: boolean; - onPressedChange: (pressed: boolean) => void; - disabled?: boolean; + pressed: boolean; + onPressedChange: (pressed: boolean) => void; + disabled?: boolean; } export type { ToggleRootProps }; diff --git a/apps/native/src/components/primitives/tooltip/index.tsx b/apps/native/src/components/primitives/tooltip/index.tsx index 5851a07..c10b380 100644 --- a/apps/native/src/components/primitives/tooltip/index.tsx +++ b/apps/native/src/components/primitives/tooltip/index.tsx @@ -1,271 +1,298 @@ -import * as React from 'react'; +import * as React from "react"; import { - BackHandler, - type GestureResponderEvent, - type LayoutChangeEvent, - type LayoutRectangle, - Pressable, - View, -} from 'react-native'; -import { type LayoutPosition, useControllableState, useRelativePosition } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; + BackHandler, + type GestureResponderEvent, + type LayoutChangeEvent, + type LayoutRectangle, + Pressable, + View, +} from "react-native"; +import { + type LayoutPosition, + useControllableState, + useRelativePosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableViewProps, - ViewRef, -} from '~/components/primitives/types'; + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableViewProps, + ViewRef, +} from "~/components/primitives/types"; import type { - RootContext as RootContextType, - TooltipOverlayProps, - TooltipPortalProps, - TooltipRootProps, -} from './types'; + RootContext as RootContextType, + TooltipOverlayProps, + TooltipPortalProps, + TooltipRootProps, +} from "./types"; interface IRootContext extends RootContextType { - triggerPosition: LayoutPosition | null; - setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; - contentLayout: LayoutRectangle | null; - setContentLayout: (contentLayout: LayoutRectangle | null) => void; - nativeID: string; + triggerPosition: LayoutPosition | null; + setTriggerPosition: (triggerPosition: LayoutPosition | null) => void; + contentLayout: LayoutRectangle | null; + setContentLayout: (contentLayout: LayoutRectangle | null) => void; + nativeID: string; } const RootContext = React.createContext(null); const Root = React.forwardRef( - ( - { - asChild, - defaultOpen, - open: openProp, - onOpenChange: onOpenChangeProp, - delayDuration: _delayDuration, - skipDelayDuration: _skipDelayDuration, - disableHoverableContent: _disableHoverableContent, - ...viewProps - }, - ref - ) => { - const nativeID = React.useId(); - const [triggerPosition, setTriggerPosition] = React.useState(null); - const [contentLayout, setContentLayout] = React.useState(null); + ( + { + asChild, + defaultOpen, + open: openProp, + onOpenChange: onOpenChangeProp, + delayDuration: _delayDuration, + skipDelayDuration: _skipDelayDuration, + disableHoverableContent: _disableHoverableContent, + ...viewProps + }, + ref, + ) => { + const nativeID = React.useId(); + const [triggerPosition, setTriggerPosition] = + React.useState(null); + const [contentLayout, setContentLayout] = + React.useState(null); - const [open = false, onOpenChange] = useControllableState({ - prop: openProp, - defaultProp: defaultOpen, - onChange: onOpenChangeProp, - }); + const [open = false, onOpenChange] = useControllableState({ + prop: openProp, + defaultProp: defaultOpen, + onChange: onOpenChangeProp, + }); - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } + const Component = asChild ? Slot.View : View; + return ( + + + + ); + }, ); -Root.displayName = 'RootNativeTooltip'; +Root.displayName = "RootNativeTooltip"; function useTooltipContext() { - const context = React.useContext(RootContext); - if (!context) { - throw new Error('Tooltip compound components cannot be rendered outside the Tooltip component'); - } - return context; + const context = React.useContext(RootContext); + if (!context) { + throw new Error( + "Tooltip compound components cannot be rendered outside the Tooltip component", + ); + } + return context; } const Trigger = React.forwardRef( - ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { - const triggerRef = React.useRef(null); - const { open, onOpenChange, setTriggerPosition } = useTooltipContext(); + ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { + const triggerRef = React.useRef(null); + const { open, onOpenChange, setTriggerPosition } = useTooltipContext(); - React.useImperativeHandle( - ref, - () => { - if (!triggerRef.current) { - return new View({}); - } - return triggerRef.current; - }, - [triggerRef.current] - ); + React.useImperativeHandle( + ref, + () => { + if (!triggerRef.current) { + return new View({}); + } + return triggerRef.current; + }, + [triggerRef.current], + ); - function onPress(ev: GestureResponderEvent) { - if (disabled) return; - triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { - setTriggerPosition({ width, pageX, pageY: pageY, height }); - }); - const newValue = !open; - onOpenChange(newValue); - onPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + if (disabled) return; + triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => { + setTriggerPosition({ width, pageX, pageY: pageY, height }); + }); + const newValue = !open; + onOpenChange(newValue); + onPressProp?.(ev); + } - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); - } + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); + }, ); -Trigger.displayName = 'TriggerNativeTooltip'; +Trigger.displayName = "TriggerNativeTooltip"; /** * @warning when using a custom ``, you might have to adjust the Content's sideOffset to account for nav elements like headers. */ function Portal({ forceMount, hostName, children }: TooltipPortalProps) { - const value = useTooltipContext(); + const value = useTooltipContext(); - if (!value.triggerPosition) { - return null; - } + if (!value.triggerPosition) { + return null; + } - if (!forceMount) { - if (!value.open) { - return null; - } - } + if (!forceMount) { + if (!value.open) { + return null; + } + } - return ( - - {children} - - ); + return ( + + {children} + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => { - const { open, onOpenChange, setContentLayout, setTriggerPosition } = useTooltipContext(); +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & TooltipOverlayProps +>( + ( + { + asChild, + forceMount, + onPress: OnPressProp, + closeOnPress = true, + ...props + }, + ref, + ) => { + const { open, onOpenChange, setContentLayout, setTriggerPosition } = + useTooltipContext(); - function onPress(ev: GestureResponderEvent) { - if (closeOnPress) { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - } - OnPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + if (closeOnPress) { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + } + OnPressProp?.(ev); + } - if (!forceMount) { - if (!open) { - return null; - } - } + if (!forceMount) { + if (!open) { + return null; + } + } - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } + const Component = asChild ? Slot.Pressable : Pressable; + return ; + }, ); -Overlay.displayName = 'OverlayNativeTooltip'; +Overlay.displayName = "OverlayNativeTooltip"; /** * @info `position`, `top`, `left`, and `maxWidth` style properties are controlled internally. Opt out of this behavior on native by setting `disablePositioningStyle` to `true`. */ -const Content = React.forwardRef( - ( - { - asChild = false, - forceMount, - align = 'center', - side = 'top', - sideOffset = 0, - alignOffset = 0, - avoidCollisions = true, - onLayout: onLayoutProp, - insets, - style, - disablePositioningStyle, - ...props - }, - ref - ) => { - const { - open, - onOpenChange, - nativeID, - contentLayout, - setContentLayout, - setTriggerPosition, - triggerPosition, - } = useTooltipContext(); +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & PositionedContentProps +>( + ( + { + asChild = false, + forceMount, + align = "center", + side = "top", + sideOffset = 0, + alignOffset = 0, + avoidCollisions = true, + onLayout: onLayoutProp, + insets, + style, + disablePositioningStyle, + ...props + }, + ref, + ) => { + const { + open, + onOpenChange, + nativeID, + contentLayout, + setContentLayout, + setTriggerPosition, + triggerPosition, + } = useTooltipContext(); - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - return true; - }); + React.useEffect(() => { + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + return true; + }, + ); - return () => { - setContentLayout(null); - backHandler.remove(); - }; - }, []); + return () => { + setContentLayout(null); + backHandler.remove(); + }; + }, []); - const positionStyle = useRelativePosition({ - align, - avoidCollisions, - triggerPosition, - contentLayout, - alignOffset, - insets, - sideOffset, - side, - disablePositioningStyle, - }); + const positionStyle = useRelativePosition({ + align, + avoidCollisions, + triggerPosition, + contentLayout, + alignOffset, + insets, + sideOffset, + side, + disablePositioningStyle, + }); - function onLayout(event: LayoutChangeEvent) { - setContentLayout(event.nativeEvent.layout); - onLayoutProp?.(event); - } + function onLayout(event: LayoutChangeEvent) { + setContentLayout(event.nativeEvent.layout); + onLayoutProp?.(event); + } - if (!forceMount) { - if (!open) { - return null; - } - } + if (!forceMount) { + if (!open) { + return null; + } + } - const Component = asChild ? Slot.View : View; - return ( - - ); - } + const Component = asChild ? Slot.View : View; + return ( + + ); + }, ); -Content.displayName = 'ContentNativeTooltip'; +Content.displayName = "ContentNativeTooltip"; export { Content, Overlay, Portal, Root, Trigger }; function onStartShouldSetResponder() { - return true; + return true; } diff --git a/apps/native/src/components/primitives/tooltip/types.ts b/apps/native/src/components/primitives/tooltip/types.ts index ff63bf0..0de82f2 100644 --- a/apps/native/src/components/primitives/tooltip/types.ts +++ b/apps/native/src/components/primitives/tooltip/types.ts @@ -1,44 +1,49 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface RootContext extends TooltipRootProps { - open: boolean; - onOpenChange: (value: boolean) => void; + open: boolean; + onOpenChange: (value: boolean) => void; } interface TooltipRootProps { - defaultOpen?: boolean; - open?: boolean; - onOpenChange?: (value: boolean) => void; - /** - * Platform: WEB ONLY - * @default 700 - */ - delayDuration?: number; - /** - * Platform: WEB ONLY - * @default 300 - */ - skipDelayDuration?: number; - /** - * Platform: WEB ONLY - */ - disableHoverableContent?: boolean; + defaultOpen?: boolean; + open?: boolean; + onOpenChange?: (value: boolean) => void; + /** + * Platform: WEB ONLY + * @default 700 + */ + delayDuration?: number; + /** + * Platform: WEB ONLY + * @default 300 + */ + skipDelayDuration?: number; + /** + * Platform: WEB ONLY + */ + disableHoverableContent?: boolean; } interface TooltipPortalProps extends ForceMountable { - children: React.ReactNode; - /** - * Platform: NATIVE ONLY - */ - hostName?: string; - /** - * Platform: WEB ONLY - */ - container?: HTMLElement | null | undefined; + children: React.ReactNode; + /** + * Platform: NATIVE ONLY + */ + hostName?: string; + /** + * Platform: WEB ONLY + */ + container?: HTMLElement | null | undefined; } interface TooltipOverlayProps extends ForceMountable { - closeOnPress?: boolean; + closeOnPress?: boolean; } -export type { RootContext, TooltipOverlayProps, TooltipPortalProps, TooltipRootProps }; +export type { + RootContext, + TooltipOverlayProps, + TooltipPortalProps, + TooltipRootProps, +}; diff --git a/apps/native/src/components/primitives/types.ts b/apps/native/src/components/primitives/types.ts index 40f26e2..8915b23 100644 --- a/apps/native/src/components/primitives/types.ts +++ b/apps/native/src/components/primitives/types.ts @@ -1,7 +1,7 @@ -import type { Pressable, Text, View, ViewStyle } from 'react-native'; +import type { Pressable, Text, View, ViewStyle } from "react-native"; type ComponentPropsWithAsChild> = - React.ComponentPropsWithoutRef & { asChild?: boolean }; + React.ComponentPropsWithoutRef & { asChild?: boolean }; type ViewRef = React.ElementRef; type PressableRef = React.ElementRef; @@ -9,22 +9,22 @@ type TextRef = React.ElementRef; type SlottableViewProps = ComponentPropsWithAsChild; type SlottablePressableProps = ComponentPropsWithAsChild & { - /** - * Platform: WEB ONLY - */ - onKeyDown?: (ev: React.KeyboardEvent) => void; - /** - * Platform: WEB ONLY - */ - onKeyUp?: (ev: React.KeyboardEvent) => void; + /** + * Platform: WEB ONLY + */ + onKeyDown?: (ev: React.KeyboardEvent) => void; + /** + * Platform: WEB ONLY + */ + onKeyUp?: (ev: React.KeyboardEvent) => void; }; type SlottableTextProps = ComponentPropsWithAsChild; interface Insets { - top?: number; - bottom?: number; - left?: number; - right?: number; + top?: number; + bottom?: number; + left?: number; + right?: number; } type PointerDownOutsideEvent = CustomEvent<{ originalEvent: PointerEvent }>; @@ -35,71 +35,71 @@ type FocusOutsideEvent = CustomEvent<{ originalEvent: FocusEvent }>; * @docs For the web version, see the Radix documentation https://www.radix-ui.com/primitives */ interface PositionedContentProps { - forceMount?: true | undefined; - style?: ViewStyle; - alignOffset?: number; - insets?: Insets; - avoidCollisions?: boolean; - align?: 'start' | 'center' | 'end'; - side?: 'top' | 'bottom'; - sideOffset?: number; - /** - * Platform: NATIVE ONLY - */ - disablePositioningStyle?: boolean; - /** - * Platform: WEB ONLY - */ - loop?: boolean; - /** - * Platform: WEB ONLY - */ - onCloseAutoFocus?: (event: Event) => void; - /** - * Platform: WEB ONLY - */ - onEscapeKeyDown?: (event: KeyboardEvent) => void; - /** - * Platform: WEB ONLY - */ - onPointerDownOutside?: (event: PointerDownOutsideEvent) => void; - /** - * Platform: WEB ONLY - */ - onFocusOutside?: (event: FocusOutsideEvent) => void; - /** - * Platform: WEB ONLY - */ - onInteractOutside?: ( - event: PointerDownOutsideEvent | FocusOutsideEvent - ) => void; - /** - * Platform: WEB ONLY - */ - collisionBoundary?: Element | null | Array; - /** - * Platform: WEB ONLY - */ - sticky?: 'partial' | 'always'; - /** - * Platform: WEB ONLY - */ - hideWhenDetached?: boolean; + forceMount?: true | undefined; + style?: ViewStyle; + alignOffset?: number; + insets?: Insets; + avoidCollisions?: boolean; + align?: "start" | "center" | "end"; + side?: "top" | "bottom"; + sideOffset?: number; + /** + * Platform: NATIVE ONLY + */ + disablePositioningStyle?: boolean; + /** + * Platform: WEB ONLY + */ + loop?: boolean; + /** + * Platform: WEB ONLY + */ + onCloseAutoFocus?: (event: Event) => void; + /** + * Platform: WEB ONLY + */ + onEscapeKeyDown?: (event: KeyboardEvent) => void; + /** + * Platform: WEB ONLY + */ + onPointerDownOutside?: (event: PointerDownOutsideEvent) => void; + /** + * Platform: WEB ONLY + */ + onFocusOutside?: (event: FocusOutsideEvent) => void; + /** + * Platform: WEB ONLY + */ + onInteractOutside?: ( + event: PointerDownOutsideEvent | FocusOutsideEvent, + ) => void; + /** + * Platform: WEB ONLY + */ + collisionBoundary?: Element | null | Array; + /** + * Platform: WEB ONLY + */ + sticky?: "partial" | "always"; + /** + * Platform: WEB ONLY + */ + hideWhenDetached?: boolean; } interface ForceMountable { - forceMount?: true | undefined; + forceMount?: true | undefined; } export type { - ComponentPropsWithAsChild, - ForceMountable, - Insets, - PositionedContentProps, - PressableRef, - SlottablePressableProps, - SlottableTextProps, - SlottableViewProps, - TextRef, - ViewRef, + ComponentPropsWithAsChild, + ForceMountable, + Insets, + PositionedContentProps, + PressableRef, + SlottablePressableProps, + SlottableTextProps, + SlottableViewProps, + TextRef, + ViewRef, }; diff --git a/apps/native/src/components/primitives/utils.ts b/apps/native/src/components/primitives/utils.ts index 8453d37..8fd371c 100644 --- a/apps/native/src/components/primitives/utils.ts +++ b/apps/native/src/components/primitives/utils.ts @@ -1,61 +1,67 @@ -import type { GestureResponderEvent } from 'react-native'; +import type { GestureResponderEvent } from "react-native"; const ToggleGroupUtils = { - getIsSelected(value: string | string[] | undefined, itemValue: string) { - if (value === undefined) { - return false; - } - if (typeof value === 'string') { - return value === itemValue; - } - return value.includes(itemValue); - }, - getNewSingleValue(originalValue: string | string[] | undefined, itemValue: string) { - if (originalValue === itemValue) { - return undefined; - } - return itemValue; - }, - getNewMultipleValue(originalValue: string | string[] | undefined, itemValue: string) { - if (originalValue === undefined) { - return [itemValue]; - } - if (typeof originalValue === 'string') { - return originalValue === itemValue ? [] : [originalValue, itemValue]; - } - if (originalValue.includes(itemValue)) { - return originalValue.filter((v) => v !== itemValue); - } - return [...originalValue, itemValue]; - }, + getIsSelected(value: string | string[] | undefined, itemValue: string) { + if (value === undefined) { + return false; + } + if (typeof value === "string") { + return value === itemValue; + } + return value.includes(itemValue); + }, + getNewSingleValue( + originalValue: string | string[] | undefined, + itemValue: string, + ) { + if (originalValue === itemValue) { + return undefined; + } + return itemValue; + }, + getNewMultipleValue( + originalValue: string | string[] | undefined, + itemValue: string, + ) { + if (originalValue === undefined) { + return [itemValue]; + } + if (typeof originalValue === "string") { + return originalValue === itemValue ? [] : [originalValue, itemValue]; + } + if (originalValue.includes(itemValue)) { + return originalValue.filter((v) => v !== itemValue); + } + return [...originalValue, itemValue]; + }, }; const EmptyGestureResponderEvent: GestureResponderEvent = { - nativeEvent: { - changedTouches: [], - identifier: '0', - locationX: 0, - locationY: 0, - pageX: 0, - pageY: 0, - target: '0', - timestamp: 0, - touches: [], - }, - bubbles: false, - cancelable: false, - currentTarget: {} as any, - defaultPrevented: false, - eventPhase: 0, - persist: () => {}, - isDefaultPrevented: () => false, - isPropagationStopped: () => false, - isTrusted: false, - preventDefault: () => {}, - stopPropagation: () => {}, - target: {} as any, - timeStamp: 0, - type: '', + nativeEvent: { + changedTouches: [], + identifier: "0", + locationX: 0, + locationY: 0, + pageX: 0, + pageY: 0, + target: "0", + timestamp: 0, + touches: [], + }, + bubbles: false, + cancelable: false, + currentTarget: {} as any, + defaultPrevented: false, + eventPhase: 0, + persist: () => {}, + isDefaultPrevented: () => false, + isPropagationStopped: () => false, + isTrusted: false, + preventDefault: () => {}, + stopPropagation: () => {}, + target: {} as any, + timeStamp: 0, + type: "", }; export { ToggleGroupUtils, EmptyGestureResponderEvent }; diff --git a/apps/native/src/components/ui/accordion.tsx b/apps/native/src/components/ui/accordion.tsx index 6d545a3..1ec3b0b 100644 --- a/apps/native/src/components/ui/accordion.tsx +++ b/apps/native/src/components/ui/accordion.tsx @@ -1,126 +1,144 @@ -import * as React from 'react'; -import { Platform, Pressable, View } from 'react-native'; +import * as React from "react"; +import { Platform, Pressable, View } from "react-native"; import Animated, { - Extrapolation, - FadeIn, - FadeOutUp, - LayoutAnimationConfig, - LayoutAnimation, - LinearTransition, - interpolate, - useAnimatedStyle, - useDerivedValue, - withTiming, -} from 'react-native-reanimated'; -import { ChevronDown } from '~/components/Icons'; -import * as AccordionPrimitive from '~/components/primitives/accordion'; -import { TextClassContext } from '~/components/ui/text'; -import { cn } from '~/lib/utils'; + Extrapolation, + FadeIn, + FadeOutUp, + LayoutAnimationConfig, + LayoutAnimation, + LinearTransition, + interpolate, + useAnimatedStyle, + useDerivedValue, + withTiming, +} from "react-native-reanimated"; +import { ChevronDown } from "~/components/Icons"; +import * as AccordionPrimitive from "~/components/primitives/accordion"; +import { TextClassContext } from "~/components/ui/text"; +import { cn } from "~/lib/utils"; const Accordion = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ children, ...props }, ref) => { - return ( - - - {children} - - - ); + return ( + + + + {children} + + + + ); }); Accordion.displayName = AccordionPrimitive.Root.displayName; const AccordionItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, value, ...props }, ref) => { - return ( - - - - ); + return ( + + + + ); }); AccordionItem.displayName = AccordionPrimitive.Item.displayName; -const Trigger = Platform.OS === 'web' ? View : Pressable; +const Trigger = Platform.OS === "web" ? View : Pressable; const AccordionTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => { - const { isExpanded } = AccordionPrimitive.useItemContext(); + const { isExpanded } = AccordionPrimitive.useItemContext(); - const progress = useDerivedValue(() => - isExpanded ? withTiming(1, { duration: 250 }) : withTiming(0, { duration: 200 }) - ); - const chevronStyle = useAnimatedStyle(() => ({ - transform: [{ rotate: `${progress.value * 180}deg` }], - opacity: interpolate(progress.value, [0, 1], [1, 0.8], Extrapolation.CLAMP), - })); + const progress = useDerivedValue(() => + isExpanded + ? withTiming(1, { duration: 250 }) + : withTiming(0, { duration: 200 }), + ); + const chevronStyle = useAnimatedStyle(() => ({ + transform: [{ rotate: `${progress.value * 180}deg` }], + opacity: interpolate(progress.value, [0, 1], [1, 0.8], Extrapolation.CLAMP), + })); - return ( - - - - - <>{children} - - - - - - - - ); + return ( + + + + + <>{children} + + + + + + + + ); }); AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; const AccordionContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => { - const { isExpanded } = AccordionPrimitive.useItemContext(); - return ( - - - {children} - - - ); + const { isExpanded } = AccordionPrimitive.useItemContext(); + return ( + + + + {children} + + + + ); }); -function InnerContent({ children, className }: { children: React.ReactNode; className?: string }) { - if (Platform.OS === 'web') { - return {children}; - } - return ( - - {children} - - ); +function InnerContent({ + children, + className, +}: { children: React.ReactNode; className?: string }) { + if (Platform.OS === "web") { + return {children}; + } + return ( + + {children} + + ); } AccordionContent.displayName = AccordionPrimitive.Content.displayName; diff --git a/apps/native/src/components/ui/alert-dialog.tsx b/apps/native/src/components/ui/alert-dialog.tsx index 4622749..c34b51c 100644 --- a/apps/native/src/components/ui/alert-dialog.tsx +++ b/apps/native/src/components/ui/alert-dialog.tsx @@ -1,10 +1,10 @@ -import * as React from 'react'; -import { Platform, StyleSheet, View } from 'react-native'; -import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; -import { buttonTextVariants, buttonVariants } from '~/components/ui/button'; -import * as AlertDialogPrimitive from '~/components/primitives/alert-dialog'; -import { cn } from '~/lib/utils'; -import { TextClassContext } from '~/components/ui/text'; +import * as React from "react"; +import { Platform, StyleSheet, View } from "react-native"; +import Animated, { FadeIn, FadeOut } from "react-native-reanimated"; +import { buttonTextVariants, buttonVariants } from "~/components/ui/button"; +import * as AlertDialogPrimitive from "~/components/primitives/alert-dialog"; +import { cn } from "~/lib/utils"; +import { TextClassContext } from "~/components/ui/text"; const AlertDialog = AlertDialogPrimitive.Root; @@ -13,155 +13,176 @@ const AlertDialogTrigger = AlertDialogPrimitive.Trigger; const AlertDialogPortal = AlertDialogPrimitive.Portal; const AlertDialogOverlayWeb = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { open } = AlertDialogPrimitive.useRootContext(); - return ( - - ); + const { open } = AlertDialogPrimitive.useRootContext(); + return ( + + ); }); -AlertDialogOverlayWeb.displayName = 'AlertDialogOverlayWeb'; +AlertDialogOverlayWeb.displayName = "AlertDialogOverlayWeb"; const AlertDialogOverlayNative = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => { - return ( - - - {children} - - - ); + return ( + + + {children} + + + ); }); -AlertDialogOverlayNative.displayName = 'AlertDialogOverlayNative'; +AlertDialogOverlayNative.displayName = "AlertDialogOverlayNative"; const AlertDialogOverlay = Platform.select({ - web: AlertDialogOverlayWeb, - default: AlertDialogOverlayNative, + web: AlertDialogOverlayWeb, + default: AlertDialogOverlayNative, }); const AlertDialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { open } = AlertDialogPrimitive.useRootContext(); - - return ( - - - - - - ); + const { open } = AlertDialogPrimitive.useRootContext(); + + return ( + + + + + + ); }); AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; const AlertDialogHeader = ({ - className, - ...props + className, + ...props }: React.ComponentPropsWithoutRef) => ( - + ); -AlertDialogHeader.displayName = 'AlertDialogHeader'; +AlertDialogHeader.displayName = "AlertDialogHeader"; const AlertDialogFooter = ({ - className, - ...props + className, + ...props }: React.ComponentPropsWithoutRef) => ( - + ); -AlertDialogFooter.displayName = 'AlertDialogFooter'; +AlertDialogFooter.displayName = "AlertDialogFooter"; const AlertDialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; const AlertDialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); -AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName; const AlertDialogAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - - - + + + )); AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; const AlertDialogCancel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - - - + + + )); AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; export { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogOverlay, - AlertDialogPortal, - AlertDialogTitle, - AlertDialogTrigger, + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, }; diff --git a/apps/native/src/components/ui/avatar.tsx b/apps/native/src/components/ui/avatar.tsx index d8704c6..89d252d 100644 --- a/apps/native/src/components/ui/avatar.tsx +++ b/apps/native/src/components/ui/avatar.tsx @@ -1,43 +1,46 @@ -import * as React from 'react'; -import * as AvatarPrimitive from '~/components/primitives/avatar'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import * as AvatarPrimitive from "~/components/primitives/avatar"; +import { cn } from "~/lib/utils"; const Avatar = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); Avatar.displayName = AvatarPrimitive.Root.displayName; const AvatarImage = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AvatarImage.displayName = AvatarPrimitive.Image.displayName; const AvatarFallback = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; diff --git a/apps/native/src/components/ui/badge.tsx b/apps/native/src/components/ui/badge.tsx index 07663af..a4a5942 100644 --- a/apps/native/src/components/ui/badge.tsx +++ b/apps/native/src/components/ui/badge.tsx @@ -1,50 +1,56 @@ -import { cva, type VariantProps } from 'class-variance-authority'; -import { View } from 'react-native'; -import * as Slot from '~/components/primitives/slot'; -import type { SlottableViewProps } from '~/components/primitives/types'; -import { cn } from '~/lib/utils'; -import { TextClassContext } from '~/components/ui/text'; +import { cva, type VariantProps } from "class-variance-authority"; +import { View } from "react-native"; +import * as Slot from "~/components/primitives/slot"; +import type { SlottableViewProps } from "~/components/primitives/types"; +import { cn } from "~/lib/utils"; +import { TextClassContext } from "~/components/ui/text"; const badgeVariants = cva( - 'web:inline-flex items-center rounded-full border border-border px-2.5 py-0.5 web:transition-colors web:focus:outline-none web:focus:ring-2 web:focus:ring-ring web:focus:ring-offset-2', - { - variants: { - variant: { - default: 'border-transparent bg-primary web:hover:opacity-80 active:opacity-80', - secondary: 'border-transparent bg-secondary web:hover:opacity-80 active:opacity-80', - destructive: 'border-transparent bg-destructive web:hover:opacity-80 active:opacity-80', - outline: 'text-foreground', - }, - }, - defaultVariants: { - variant: 'default', - }, - } + "web:inline-flex items-center rounded-full border border-border px-2.5 py-0.5 web:transition-colors web:focus:outline-none web:focus:ring-2 web:focus:ring-ring web:focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary web:hover:opacity-80 active:opacity-80", + secondary: + "border-transparent bg-secondary web:hover:opacity-80 active:opacity-80", + destructive: + "border-transparent bg-destructive web:hover:opacity-80 active:opacity-80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + }, ); -const badgeTextVariants = cva('text-xs font-semibold ', { - variants: { - variant: { - default: 'text-primary-foreground', - secondary: 'text-secondary-foreground', - destructive: 'text-destructive-foreground', - outline: 'text-foreground', - }, - }, - defaultVariants: { - variant: 'default', - }, +const badgeTextVariants = cva("text-xs font-semibold ", { + variants: { + variant: { + default: "text-primary-foreground", + secondary: "text-secondary-foreground", + destructive: "text-destructive-foreground", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, }); type BadgeProps = SlottableViewProps & VariantProps; function Badge({ className, variant, asChild, ...props }: BadgeProps) { - const Component = asChild ? Slot.View : View; - return ( - - - - ); + const Component = asChild ? Slot.View : View; + return ( + + + + ); } export { Badge, badgeTextVariants, badgeVariants }; diff --git a/apps/native/src/components/ui/button.tsx b/apps/native/src/components/ui/button.tsx index 5e0e4a0..30c5523 100644 --- a/apps/native/src/components/ui/button.tsx +++ b/apps/native/src/components/ui/button.tsx @@ -1,88 +1,91 @@ -import { cva, type VariantProps } from 'class-variance-authority'; -import * as React from 'react'; -import { Pressable } from 'react-native'; -import { TextClassContext } from '~/components/ui/text'; -import { cn } from '~/lib/utils'; +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; +import { Pressable } from "react-native"; +import { TextClassContext } from "~/components/ui/text"; +import { cn } from "~/lib/utils"; const buttonVariants = cva( - 'group flex items-center justify-center rounded-md web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2', - { - variants: { - variant: { - default: 'bg-primary web:hover:opacity-90 active:opacity-90', - destructive: 'bg-destructive web:hover:opacity-90 active:opacity-90', - outline: - 'border border-input bg-background web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent', - secondary: 'bg-secondary web:hover:opacity-80 active:opacity-80', - ghost: 'web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent', - link: 'web:underline-offset-4 web:hover:underline web:focus:underline ', - }, - size: { - default: 'h-10 px-4 py-2 native:h-12 native:px-5 native:py-3', - sm: 'h-9 rounded-md px-3', - lg: 'h-11 rounded-md px-8 native:h-14', - icon: 'h-10 w-10', - }, - }, - defaultVariants: { - variant: 'default', - size: 'default', - }, - } + "group flex items-center justify-center rounded-md web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2", + { + variants: { + variant: { + default: "bg-primary web:hover:opacity-90 active:opacity-90", + destructive: "bg-destructive web:hover:opacity-90 active:opacity-90", + outline: + "border border-input bg-background web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent", + secondary: "bg-secondary web:hover:opacity-80 active:opacity-80", + ghost: + "web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent", + link: "web:underline-offset-4 web:hover:underline web:focus:underline ", + }, + size: { + default: "h-10 px-4 py-2 native:h-12 native:px-5 native:py-3", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8 native:h-14", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, ); const buttonTextVariants = cva( - 'web:whitespace-nowrap text-sm native:text-base font-medium text-foreground web:transition-colors', - { - variants: { - variant: { - default: 'text-primary-foreground', - destructive: 'text-destructive-foreground', - outline: 'group-active:text-accent-foreground', - secondary: 'text-secondary-foreground group-active:text-secondary-foreground', - ghost: 'group-active:text-accent-foreground', - link: 'text-primary group-active:underline', - }, - size: { - default: '', - sm: '', - lg: 'native:text-lg', - icon: '', - }, - }, - defaultVariants: { - variant: 'default', - size: 'default', - }, - } + "web:whitespace-nowrap text-sm native:text-base font-medium text-foreground web:transition-colors", + { + variants: { + variant: { + default: "text-primary-foreground", + destructive: "text-destructive-foreground", + outline: "group-active:text-accent-foreground", + secondary: + "text-secondary-foreground group-active:text-secondary-foreground", + ghost: "group-active:text-accent-foreground", + link: "text-primary group-active:underline", + }, + size: { + default: "", + sm: "", + lg: "native:text-lg", + icon: "", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, ); type ButtonProps = React.ComponentPropsWithoutRef & - VariantProps; + VariantProps; -const Button = React.forwardRef, ButtonProps>( - ({ className, variant, size, ...props }, ref) => { - return ( - - - - ); - } -); -Button.displayName = 'Button'; +const Button = React.forwardRef< + React.ElementRef, + ButtonProps +>(({ className, variant, size, ...props }, ref) => { + return ( + + + + ); +}); +Button.displayName = "Button"; export { Button, buttonTextVariants, buttonVariants }; export type { ButtonProps }; diff --git a/apps/native/src/components/ui/card.tsx b/apps/native/src/components/ui/card.tsx index f5a8fc2..0ba30aa 100644 --- a/apps/native/src/components/ui/card.tsx +++ b/apps/native/src/components/ui/card.tsx @@ -1,67 +1,92 @@ -import * as React from 'react'; -import { Text, View } from 'react-native'; -import { TextClassContext } from '~/components/ui/text'; -import { TextRef, ViewRef } from '~/components/primitives/types'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import { Text, View } from "react-native"; +import { TextClassContext } from "~/components/ui/text"; +import { TextRef, ViewRef } from "~/components/primitives/types"; +import { cn } from "~/lib/utils"; -const Card = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -); -Card.displayName = 'Card'; +const Card = React.forwardRef< + ViewRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Card.displayName = "Card"; -const CardHeader = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -); -CardHeader.displayName = 'CardHeader'; +const CardHeader = React.forwardRef< + ViewRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +CardHeader.displayName = "CardHeader"; -const CardTitle = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -); -CardTitle.displayName = 'CardTitle'; +const CardTitle = React.forwardRef< + TextRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +CardTitle.displayName = "CardTitle"; -const CardDescription = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -); -CardDescription.displayName = 'CardDescription'; +const CardDescription = React.forwardRef< + TextRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +CardDescription.displayName = "CardDescription"; -const CardContent = React.forwardRef>( - ({ className, ...props }, ref) => ( - - - - ) -); -CardContent.displayName = 'CardContent'; +const CardContent = React.forwardRef< + ViewRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +CardContent.displayName = "CardContent"; -const CardFooter = React.forwardRef>( - ({ className, ...props }, ref) => ( - - ) -); -CardFooter.displayName = 'CardFooter'; +const CardFooter = React.forwardRef< + ViewRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +CardFooter.displayName = "CardFooter"; -export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }; +export { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +}; diff --git a/apps/native/src/components/ui/checkbox.tsx b/apps/native/src/components/ui/checkbox.tsx index f43608d..f641f33 100644 --- a/apps/native/src/components/ui/checkbox.tsx +++ b/apps/native/src/components/ui/checkbox.tsx @@ -1,33 +1,35 @@ -import { Check } from '~/components/Icons'; -import * as React from 'react'; -import * as CheckboxPrimitive from '~/components/primitives/checkbox'; +import { Check } from "~/components/Icons"; +import * as React from "react"; +import * as CheckboxPrimitive from "~/components/primitives/checkbox"; -import { Platform } from 'react-native'; -import { cn } from '~/lib/utils'; +import { Platform } from "react-native"; +import { cn } from "~/lib/utils"; const Checkbox = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - return ( - - - - - - ); + return ( + + + + + + ); }); Checkbox.displayName = CheckboxPrimitive.Root.displayName; diff --git a/apps/native/src/components/ui/collapsible.tsx b/apps/native/src/components/ui/collapsible.tsx index b75f3a4..5f08571 100644 --- a/apps/native/src/components/ui/collapsible.tsx +++ b/apps/native/src/components/ui/collapsible.tsx @@ -1,4 +1,4 @@ -import * as CollapsiblePrimitive from '~/components/primitives/collapsible'; +import * as CollapsiblePrimitive from "~/components/primitives/collapsible"; const Collapsible = CollapsiblePrimitive.Root; diff --git a/apps/native/src/components/ui/context-menu.tsx b/apps/native/src/components/ui/context-menu.tsx index 8633164..9abdbc2 100644 --- a/apps/native/src/components/ui/context-menu.tsx +++ b/apps/native/src/components/ui/context-menu.tsx @@ -1,17 +1,17 @@ import { - Check, - ChevronDown, - ChevronRight, - ChevronUp, + Check, + ChevronDown, + ChevronRight, + ChevronUp, } from "~/components/Icons"; import * as React from "react"; import { - Platform, - type StyleProp, - StyleSheet, - Text, - View, - type ViewStyle, + Platform, + type StyleProp, + StyleSheet, + Text, + View, + type ViewStyle, } from "react-native"; import * as ContextMenuPrimitive from "~/components/primitives/context-menu"; import { TextClassContext } from "~/components/ui/text"; @@ -30,229 +30,229 @@ const ContextMenuSub = ContextMenuPrimitive.Sub; const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup; const ContextMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, children, ...props }, ref) => { - const { open } = ContextMenuPrimitive.useSubContext(); - const Icon = - Platform.OS === "web" ? ChevronRight : open ? ChevronUp : ChevronDown; - return ( - - - <>{children} - - - - ); + const { open } = ContextMenuPrimitive.useSubContext(); + const Icon = + Platform.OS === "web" ? ChevronRight : open ? ChevronUp : ChevronDown; + return ( + + + <>{children} + + + + ); }); ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName; const ContextMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { open } = ContextMenuPrimitive.useSubContext(); - return ( - - ); + const { open } = ContextMenuPrimitive.useSubContext(); + return ( + + ); }); ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName; const ContextMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - overlayStyle?: StyleProp; - overlayClassName?: string; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + overlayStyle?: StyleProp; + overlayClassName?: string; + } >(({ className, overlayClassName, overlayStyle, ...props }, ref) => { - const { open } = ContextMenuPrimitive.useRootContext(); - return ( - - - - - - ); + const { open } = ContextMenuPrimitive.useRootContext(); + return ( + + + + + + ); }); ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName; const ContextMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - - - + + + )); ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName; const ContextMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, checked, ...props }, ref) => ( - - - - - - - <>{children} - + + + + + + + <>{children} + )); ContextMenuCheckboxItem.displayName = - ContextMenuPrimitive.CheckboxItem.displayName; + ContextMenuPrimitive.CheckboxItem.displayName; const ContextMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - - - - - - <>{children} - + + + + + + + <>{children} + )); ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName; const ContextMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - + )); ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName; const ContextMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName; const ContextMenuShortcut = ({ - className, - ...props + className, + ...props }: React.ComponentPropsWithoutRef) => { - return ( - - ); + return ( + + ); }; ContextMenuShortcut.displayName = "ContextMenuShortcut"; export { - ContextMenu, - ContextMenuCheckboxItem, - ContextMenuContent, - ContextMenuGroup, - ContextMenuItem, - ContextMenuLabel, - ContextMenuPortal, - ContextMenuRadioGroup, - ContextMenuRadioItem, - ContextMenuSeparator, - ContextMenuShortcut, - ContextMenuSub, - ContextMenuSubContent, - ContextMenuSubTrigger, - ContextMenuTrigger, + ContextMenu, + ContextMenuCheckboxItem, + ContextMenuContent, + ContextMenuGroup, + ContextMenuItem, + ContextMenuLabel, + ContextMenuPortal, + ContextMenuRadioGroup, + ContextMenuRadioItem, + ContextMenuSeparator, + ContextMenuShortcut, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuTrigger, }; diff --git a/apps/native/src/components/ui/dialog.tsx b/apps/native/src/components/ui/dialog.tsx index 25df5c9..4dca4e6 100644 --- a/apps/native/src/components/ui/dialog.tsx +++ b/apps/native/src/components/ui/dialog.tsx @@ -1,9 +1,9 @@ -import { X } from '~/components/Icons'; -import * as React from 'react'; -import { Platform, StyleSheet, View } from 'react-native'; -import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; -import * as DialogPrimitive from '~/components/primitives/dialog'; -import { cn } from '~/lib/utils'; +import { X } from "~/components/Icons"; +import * as React from "react"; +import { Platform, StyleSheet, View } from "react-native"; +import Animated, { FadeIn, FadeOut } from "react-native-reanimated"; +import * as DialogPrimitive from "~/components/primitives/dialog"; +import { cn } from "~/lib/utils"; const Dialog = DialogPrimitive.Root; @@ -14,137 +14,160 @@ const DialogPortal = DialogPrimitive.Portal; const DialogClose = DialogPrimitive.Close; const DialogOverlayWeb = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { open } = DialogPrimitive.useRootContext(); - return ( - - ); + const { open } = DialogPrimitive.useRootContext(); + return ( + + ); }); -DialogOverlayWeb.displayName = 'DialogOverlayWeb'; +DialogOverlayWeb.displayName = "DialogOverlayWeb"; const DialogOverlayNative = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => { - return ( - - - <>{children} - - - ); + return ( + + + <>{children} + + + ); }); -DialogOverlayNative.displayName = 'DialogOverlayNative'; +DialogOverlayNative.displayName = "DialogOverlayNative"; const DialogOverlay = Platform.select({ - web: DialogOverlayWeb, - default: DialogOverlayNative, + web: DialogOverlayWeb, + default: DialogOverlayNative, }); const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => { - const { open } = DialogPrimitive.useRootContext(); - return ( - - - - {children} - - - - - - - ); + const { open } = DialogPrimitive.useRootContext(); + return ( + + + + {children} + + + + + + + ); }); DialogContent.displayName = DialogPrimitive.Content.displayName; -const DialogHeader = ({ className, ...props }: React.ComponentPropsWithoutRef) => ( - +const DialogHeader = ({ + className, + ...props +}: React.ComponentPropsWithoutRef) => ( + ); -DialogHeader.displayName = 'DialogHeader'; +DialogHeader.displayName = "DialogHeader"; -const DialogFooter = ({ className, ...props }: React.ComponentPropsWithoutRef) => ( - +const DialogFooter = ({ + className, + ...props +}: React.ComponentPropsWithoutRef) => ( + ); -DialogFooter.displayName = 'DialogFooter'; +DialogFooter.displayName = "DialogFooter"; const DialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DialogTitle.displayName = DialogPrimitive.Title.displayName; const DialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DialogDescription.displayName = DialogPrimitive.Description.displayName; export { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogOverlay, - DialogPortal, - DialogTitle, - DialogTrigger, + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, }; diff --git a/apps/native/src/components/ui/dropdown-menu.tsx b/apps/native/src/components/ui/dropdown-menu.tsx index fbbbe75..44ca6fb 100644 --- a/apps/native/src/components/ui/dropdown-menu.tsx +++ b/apps/native/src/components/ui/dropdown-menu.tsx @@ -1,17 +1,17 @@ import { - Check, - ChevronDown, - ChevronRight, - ChevronUp, + Check, + ChevronDown, + ChevronRight, + ChevronUp, } from "~/components/Icons"; import * as React from "react"; import { - Platform, - type StyleProp, - StyleSheet, - Text, - View, - type ViewStyle, + Platform, + type StyleProp, + StyleSheet, + Text, + View, + type ViewStyle, } from "react-native"; import * as DropdownMenuPrimitive from "~/components/primitives/dropdown-menu"; import { TextClassContext } from "~/components/ui/text"; @@ -30,231 +30,231 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub; const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, children, ...props }, ref) => { - const { open } = DropdownMenuPrimitive.useSubContext(); - const Icon = - Platform.OS === "web" ? ChevronRight : open ? ChevronUp : ChevronDown; - return ( - - - <>{children} - - - - ); + const { open } = DropdownMenuPrimitive.useSubContext(); + const Icon = + Platform.OS === "web" ? ChevronRight : open ? ChevronUp : ChevronDown; + return ( + + + <>{children} + + + + ); }); DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName; + DropdownMenuPrimitive.SubTrigger.displayName; const DropdownMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { open } = DropdownMenuPrimitive.useSubContext(); - return ( - - ); + const { open } = DropdownMenuPrimitive.useSubContext(); + return ( + + ); }); DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName; + DropdownMenuPrimitive.SubContent.displayName; const DropdownMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - overlayStyle?: StyleProp; - overlayClassName?: string; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + overlayStyle?: StyleProp; + overlayClassName?: string; + } >(({ className, overlayClassName, overlayStyle, ...props }, ref) => { - const { open } = DropdownMenuPrimitive.useRootContext(); - return ( - - - - - - ); + const { open } = DropdownMenuPrimitive.useRootContext(); + return ( + + + + + + ); }); DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; const DropdownMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - - - + + + )); DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, checked, ...props }, ref) => ( - - - - - - - <>{children} - + + + + + + + <>{children} + )); DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName; + DropdownMenuPrimitive.CheckboxItem.displayName; const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - - - - - - <>{children} - + + + + + + + <>{children} + )); DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; const DropdownMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - + )); DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; const DropdownMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; const DropdownMenuShortcut = ({ - className, - ...props + className, + ...props }: React.ComponentPropsWithoutRef) => { - return ( - - ); + return ( + + ); }; DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; export { - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuPortal, - DropdownMenuRadioGroup, - DropdownMenuRadioItem, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuTrigger, + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, }; diff --git a/apps/native/src/components/ui/hover-card.tsx b/apps/native/src/components/ui/hover-card.tsx index b5bb872..82c2d33 100644 --- a/apps/native/src/components/ui/hover-card.tsx +++ b/apps/native/src/components/ui/hover-card.tsx @@ -1,44 +1,44 @@ -import * as React from 'react'; -import { Platform, StyleSheet } from 'react-native'; -import Animated, { FadeIn } from 'react-native-reanimated'; -import { TextClassContext } from '~/components/ui/text'; -import * as HoverCardPrimitive from '~/components/primitives/hover-card'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import { Platform, StyleSheet } from "react-native"; +import Animated, { FadeIn } from "react-native-reanimated"; +import { TextClassContext } from "~/components/ui/text"; +import * as HoverCardPrimitive from "~/components/primitives/hover-card"; +import { cn } from "~/lib/utils"; const HoverCard = HoverCardPrimitive.Root; const HoverCardTrigger = HoverCardPrimitive.Trigger; const HoverCardContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, align = 'center', sideOffset = 4, ...props }, ref) => { - const { open } = HoverCardPrimitive.useRootContext(); - return ( - - - - - - - - - - ); + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => { + const { open } = HoverCardPrimitive.useRootContext(); + return ( + + + + + + + + + + ); }); HoverCardContent.displayName = HoverCardPrimitive.Content.displayName; diff --git a/apps/native/src/components/ui/input.tsx b/apps/native/src/components/ui/input.tsx index 0537c1a..c19d7b7 100644 --- a/apps/native/src/components/ui/input.tsx +++ b/apps/native/src/components/ui/input.tsx @@ -1,26 +1,26 @@ -import * as React from 'react'; -import { TextInput } from 'react-native'; +import * as React from "react"; +import { TextInput } from "react-native"; -import { cn } from '~/lib/utils'; +import { cn } from "~/lib/utils"; const Input = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, placeholderClassName, ...props }, ref) => { - return ( - - ); + return ( + + ); }); -Input.displayName = 'Input'; +Input.displayName = "Input"; export { Input }; diff --git a/apps/native/src/components/ui/label.tsx b/apps/native/src/components/ui/label.tsx index d313096..6a5cb78 100644 --- a/apps/native/src/components/ui/label.tsx +++ b/apps/native/src/components/ui/label.tsx @@ -1,28 +1,33 @@ -import * as LabelPrimitive from '~/components/primitives/label'; -import * as React from 'react'; -import { cn } from '~/lib/utils'; +import * as LabelPrimitive from "~/components/primitives/label"; +import * as React from "react"; +import { cn } from "~/lib/utils"; const Label = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, onPress, onLongPress, onPressIn, onPressOut, ...props }, ref) => ( - - - -)); + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, onPress, onLongPress, onPressIn, onPressOut, ...props }, + ref, + ) => ( + + + + ), +); Label.displayName = LabelPrimitive.Root.displayName; export { Label }; diff --git a/apps/native/src/components/ui/menubar.tsx b/apps/native/src/components/ui/menubar.tsx index ed11dc1..8fa9fb3 100644 --- a/apps/native/src/components/ui/menubar.tsx +++ b/apps/native/src/components/ui/menubar.tsx @@ -1,9 +1,14 @@ -import { Check, ChevronDown, ChevronRight, ChevronUp } from '~/components/Icons'; -import * as React from 'react'; -import { Platform, Text, View } from 'react-native'; -import * as MenubarPrimitive from '~/components/primitives/menubar'; -import { TextClassContext } from '~/components/ui/text'; -import { cn } from '~/lib/utils'; +import { + Check, + ChevronDown, + ChevronRight, + ChevronUp, +} from "~/components/Icons"; +import * as React from "react"; +import { Platform, Text, View } from "react-native"; +import * as MenubarPrimitive from "~/components/primitives/menubar"; +import { TextClassContext } from "~/components/ui/text"; +import { cn } from "~/lib/utils"; const MenubarMenu = MenubarPrimitive.Menu; @@ -16,245 +21,249 @@ const MenubarSub = MenubarPrimitive.Sub; const MenubarRadioGroup = MenubarPrimitive.RadioGroup; const Menubar = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); Menubar.displayName = MenubarPrimitive.Root.displayName; const MenubarTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { value } = MenubarPrimitive.useRootContext(); - const { value: itemValue } = MenubarPrimitive.useMenuContext(); + const { value } = MenubarPrimitive.useRootContext(); + const { value: itemValue } = MenubarPrimitive.useMenuContext(); - return ( - - ); + return ( + + ); }); MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName; const MenubarSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, children, ...props }, ref) => { - const { open } = MenubarPrimitive.useSubContext(); - const Icon = Platform.OS === 'web' ? ChevronRight : open ? ChevronUp : ChevronDown; - return ( - - - <>{children} - - - - ); + const { open } = MenubarPrimitive.useSubContext(); + const Icon = + Platform.OS === "web" ? ChevronRight : open ? ChevronUp : ChevronDown; + return ( + + + <>{children} + + + + ); }); MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName; const MenubarSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { open } = MenubarPrimitive.useSubContext(); - return ( - - ); + const { open } = MenubarPrimitive.useSubContext(); + return ( + + ); }); MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName; const MenubarContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { value } = MenubarPrimitive.useRootContext(); - const { value: itemValue } = MenubarPrimitive.useMenuContext(); - return ( - - - - ); + const { value } = MenubarPrimitive.useRootContext(); + const { value: itemValue } = MenubarPrimitive.useMenuContext(); + return ( + + + + ); }); MenubarContent.displayName = MenubarPrimitive.Content.displayName; const MenubarItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - - - + + + )); MenubarItem.displayName = MenubarPrimitive.Item.displayName; const MenubarCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, checked, ...props }, ref) => ( - - - - - - - <>{children} - + + + + + + + <>{children} + )); MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName; const MenubarRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - - - - - - <>{children} - + + + + + + + <>{children} + )); MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName; const MenubarLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - + )); MenubarLabel.displayName = MenubarPrimitive.Label.displayName; const MenubarSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName; -const MenubarShortcut = ({ className, ...props }: React.ComponentPropsWithoutRef) => { - return ( - - ); +const MenubarShortcut = ({ + className, + ...props +}: React.ComponentPropsWithoutRef) => { + return ( + + ); }; -MenubarShortcut.displayName = 'MenubarShortcut'; +MenubarShortcut.displayName = "MenubarShortcut"; export { - Menubar, - MenubarCheckboxItem, - MenubarContent, - MenubarGroup, - MenubarItem, - MenubarLabel, - MenubarMenu, - MenubarPortal, - MenubarRadioGroup, - MenubarRadioItem, - MenubarSeparator, - MenubarShortcut, - MenubarSub, - MenubarSubContent, - MenubarSubTrigger, - MenubarTrigger, + Menubar, + MenubarCheckboxItem, + MenubarContent, + MenubarGroup, + MenubarItem, + MenubarLabel, + MenubarMenu, + MenubarPortal, + MenubarRadioGroup, + MenubarRadioItem, + MenubarSeparator, + MenubarShortcut, + MenubarSub, + MenubarSubContent, + MenubarSubTrigger, + MenubarTrigger, }; diff --git a/apps/native/src/components/ui/navigation-menu.tsx b/apps/native/src/components/ui/navigation-menu.tsx index 6b9f543..c45df65 100644 --- a/apps/native/src/components/ui/navigation-menu.tsx +++ b/apps/native/src/components/ui/navigation-menu.tsx @@ -1,177 +1,188 @@ -import { cva } from 'class-variance-authority'; -import { ChevronDown } from '~/components/Icons'; -import * as React from 'react'; -import { Platform, View } from 'react-native'; +import { cva } from "class-variance-authority"; +import { ChevronDown } from "~/components/Icons"; +import * as React from "react"; +import { Platform, View } from "react-native"; import Animated, { - Extrapolation, - FadeInLeft, - FadeOutLeft, - interpolate, - useAnimatedStyle, - useDerivedValue, - withTiming, -} from 'react-native-reanimated'; -import * as NavigationMenuPrimitive from '~/components/primitives/navigation-menu'; -import { cn } from '~/lib/utils'; + Extrapolation, + FadeInLeft, + FadeOutLeft, + interpolate, + useAnimatedStyle, + useDerivedValue, + withTiming, +} from "react-native-reanimated"; +import * as NavigationMenuPrimitive from "~/components/primitives/navigation-menu"; +import { cn } from "~/lib/utils"; const NavigationMenu = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - {children} - {Platform.OS === 'web' && } - + + {children} + {Platform.OS === "web" && } + )); NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName; const NavigationMenuList = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName; const NavigationMenuItem = NavigationMenuPrimitive.Item; const navigationMenuTriggerStyle = cva( - 'web:group web:inline-flex flex-row h-10 native:h-12 native:px-3 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium web:transition-colors web:hover:bg-accent active:bg-accent web:hover:text-accent-foreground web:focus:bg-accent web:focus:text-accent-foreground web:focus:outline-none web:disabled:pointer-events-none disabled:opacity-50 web:data-[active]:bg-accent/50 web:data-[state=open]:bg-accent/50' + "web:group web:inline-flex flex-row h-10 native:h-12 native:px-3 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium web:transition-colors web:hover:bg-accent active:bg-accent web:hover:text-accent-foreground web:focus:bg-accent web:focus:text-accent-foreground web:focus:outline-none web:disabled:pointer-events-none disabled:opacity-50 web:data-[active]:bg-accent/50 web:data-[state=open]:bg-accent/50", ); const NavigationMenuTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => { - const { value } = NavigationMenuPrimitive.useRootContext(); - const { value: itemValue } = NavigationMenuPrimitive.useItemContext(); + const { value } = NavigationMenuPrimitive.useRootContext(); + const { value: itemValue } = NavigationMenuPrimitive.useItemContext(); - const progress = useDerivedValue(() => - value === itemValue ? withTiming(1, { duration: 250 }) : withTiming(0, { duration: 200 }) - ); - const chevronStyle = useAnimatedStyle(() => ({ - transform: [{ rotate: `${progress.value * 180}deg` }], - opacity: interpolate(progress.value, [0, 1], [1, 0.8], Extrapolation.CLAMP), - })); + const progress = useDerivedValue(() => + value === itemValue + ? withTiming(1, { duration: 250 }) + : withTiming(0, { duration: 200 }), + ); + const chevronStyle = useAnimatedStyle(() => ({ + transform: [{ rotate: `${progress.value * 180}deg` }], + opacity: interpolate(progress.value, [0, 1], [1, 0.8], Extrapolation.CLAMP), + })); - return ( - - <>{children} - - - - - ); + return ( + + <>{children} + + + + + ); }); NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName; const NavigationMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => { - const { value } = NavigationMenuPrimitive.useRootContext(); - const { value: itemValue } = NavigationMenuPrimitive.useItemContext(); - return ( - - - - {children} - - - - ); + const { value } = NavigationMenuPrimitive.useRootContext(); + const { value: itemValue } = NavigationMenuPrimitive.useItemContext(); + return ( + + + + {children} + + + + ); }); NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName; const NavigationMenuLink = NavigationMenuPrimitive.Link; const NavigationMenuViewport = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - return ( - - - - ); + return ( + + + + ); }); -NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName; +NavigationMenuViewport.displayName = + NavigationMenuPrimitive.Viewport.displayName; const NavigationMenuIndicator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { value } = NavigationMenuPrimitive.useRootContext(); - const { value: itemValue } = NavigationMenuPrimitive.useItemContext(); + const { value } = NavigationMenuPrimitive.useRootContext(); + const { value: itemValue } = NavigationMenuPrimitive.useItemContext(); - return ( - - - - ); + return ( + + + + ); }); -NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName; +NavigationMenuIndicator.displayName = + NavigationMenuPrimitive.Indicator.displayName; export { - NavigationMenu, - NavigationMenuContent, - NavigationMenuIndicator, - NavigationMenuItem, - NavigationMenuLink, - NavigationMenuList, - NavigationMenuTrigger, - NavigationMenuViewport, - navigationMenuTriggerStyle, + NavigationMenu, + NavigationMenuContent, + NavigationMenuIndicator, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + NavigationMenuTrigger, + NavigationMenuViewport, + navigationMenuTriggerStyle, }; diff --git a/apps/native/src/components/ui/popover.tsx b/apps/native/src/components/ui/popover.tsx index bdf82b9..1bfbf1c 100644 --- a/apps/native/src/components/ui/popover.tsx +++ b/apps/native/src/components/ui/popover.tsx @@ -1,38 +1,40 @@ -import * as React from 'react'; -import { Platform, StyleSheet } from 'react-native'; -import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; -import { TextClassContext } from '~/components/ui/text'; -import * as PopoverPrimitive from '~/components/primitives/popover'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import { Platform, StyleSheet } from "react-native"; +import Animated, { FadeIn, FadeOut } from "react-native-reanimated"; +import { TextClassContext } from "~/components/ui/text"; +import * as PopoverPrimitive from "~/components/primitives/popover"; +import { cn } from "~/lib/utils"; const Popover = PopoverPrimitive.Root; const PopoverTrigger = PopoverPrimitive.Trigger; const PopoverContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, align = 'center', sideOffset = 4, ...props }, ref) => { - return ( - - - - - - - - - - ); + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => { + return ( + + + + + + + + + + ); }); PopoverContent.displayName = PopoverPrimitive.Content.displayName; diff --git a/apps/native/src/components/ui/radio-group.tsx b/apps/native/src/components/ui/radio-group.tsx index 01757c4..20e473b 100644 --- a/apps/native/src/components/ui/radio-group.tsx +++ b/apps/native/src/components/ui/radio-group.tsx @@ -1,37 +1,41 @@ -import * as React from 'react'; -import { View } from 'react-native'; -import * as RadioGroupPrimitive from '~/components/primitives/radio-group'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import { View } from "react-native"; +import * as RadioGroupPrimitive from "~/components/primitives/radio-group"; +import { cn } from "~/lib/utils"; const RadioGroup = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - return ( - - ); + return ( + + ); }); RadioGroup.displayName = RadioGroupPrimitive.Root.displayName; const RadioGroupItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - return ( - - - - - - ); + return ( + + + + + + ); }); RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName; diff --git a/apps/native/src/components/ui/select.tsx b/apps/native/src/components/ui/select.tsx index c18b6dd..245f8fc 100644 --- a/apps/native/src/components/ui/select.tsx +++ b/apps/native/src/components/ui/select.tsx @@ -1,9 +1,9 @@ -import { Check, ChevronDown, ChevronUp } from '~/components/Icons'; -import * as React from 'react'; -import { Platform, StyleSheet, View } from 'react-native'; -import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; -import * as SelectPrimitive from '~/components/primitives/select'; -import { cn } from '~/lib/utils'; +import { Check, ChevronDown, ChevronUp } from "~/components/Icons"; +import * as React from "react"; +import { Platform, StyleSheet, View } from "react-native"; +import Animated, { FadeIn, FadeOut } from "react-native-reanimated"; +import * as SelectPrimitive from "~/components/primitives/select"; +import { cn } from "~/lib/utils"; type Option = SelectPrimitive.Option; @@ -14,21 +14,25 @@ const SelectGroup = SelectPrimitive.Group; const SelectValue = SelectPrimitive.Value; const SelectTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - span]:line-clamp-1', - props.disabled && 'web:cursor-not-allowed opacity-50', - className - )} - {...props} - > - <>{children} - - + span]:line-clamp-1", + props.disabled && "web:cursor-not-allowed opacity-50", + className, + )} + {...props} + > + <>{children} + + )); SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; @@ -36,146 +40,154 @@ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; * Platform: WEB ONLY */ const SelectScrollUpButton = ({ - className, - ...props + className, + ...props }: React.ComponentPropsWithoutRef) => { - if (Platform.OS !== 'web') { - return null; - } - return ( - - - - ); + if (Platform.OS !== "web") { + return null; + } + return ( + + + + ); }; /** * Platform: WEB ONLY */ const SelectScrollDownButton = ({ - className, - ...props + className, + ...props }: React.ComponentPropsWithoutRef) => { - if (Platform.OS !== 'web') { - return null; - } - return ( - - - - ); + if (Platform.OS !== "web") { + return null; + } + return ( + + + + ); }; const SelectContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, position = 'popper', ...props }, ref) => { - const { open } = SelectPrimitive.useRootContext(); + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => { + const { open } = SelectPrimitive.useRootContext(); - return ( - - - - - - - {children} - - - - - - - ); + return ( + + + + + + + {children} + + + + + + + ); }); SelectContent.displayName = SelectPrimitive.Content.displayName; const SelectLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); SelectLabel.displayName = SelectPrimitive.Label.displayName; const SelectItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - - - - - + + + + + + - - + + )); SelectItem.displayName = SelectPrimitive.Item.displayName; const SelectSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); SelectSeparator.displayName = SelectPrimitive.Separator.displayName; export { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectScrollDownButton, - SelectScrollUpButton, - SelectSeparator, - SelectTrigger, - SelectValue, - type Option, + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue, + type Option, }; diff --git a/apps/native/src/components/ui/separator.tsx b/apps/native/src/components/ui/separator.tsx index 318770e..bad1b95 100644 --- a/apps/native/src/components/ui/separator.tsx +++ b/apps/native/src/components/ui/separator.tsx @@ -1,23 +1,28 @@ -import * as React from 'react'; -import * as SeparatorPrimitive from '~/components/primitives/separator'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import * as SeparatorPrimitive from "~/components/primitives/separator"; +import { cn } from "~/lib/utils"; const Separator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => ( - -)); + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref, + ) => ( + + ), +); Separator.displayName = SeparatorPrimitive.Root.displayName; export { Separator }; diff --git a/apps/native/src/components/ui/skeleton.tsx b/apps/native/src/components/ui/skeleton.tsx index 1955e7a..1e174a8 100644 --- a/apps/native/src/components/ui/skeleton.tsx +++ b/apps/native/src/components/ui/skeleton.tsx @@ -1,39 +1,39 @@ -import * as React from 'react'; +import * as React from "react"; import Animated, { - useAnimatedStyle, - useSharedValue, - withRepeat, - withSequence, - withTiming, -} from 'react-native-reanimated'; -import { cn } from '~/lib/utils'; + useAnimatedStyle, + useSharedValue, + withRepeat, + withSequence, + withTiming, +} from "react-native-reanimated"; +import { cn } from "~/lib/utils"; const duration = 1000; function Skeleton({ - className, - ...props -}: Omit, 'style'>) { - const sv = useSharedValue(1); + className, + ...props +}: Omit, "style">) { + const sv = useSharedValue(1); - React.useEffect(() => { - sv.value = withRepeat( - withSequence(withTiming(0.5, { duration }), withTiming(1, { duration })), - -1 - ); - }, []); + React.useEffect(() => { + sv.value = withRepeat( + withSequence(withTiming(0.5, { duration }), withTiming(1, { duration })), + -1, + ); + }, []); - const style = useAnimatedStyle(() => ({ - opacity: sv.value, - })); + const style = useAnimatedStyle(() => ({ + opacity: sv.value, + })); - return ( - - ); + return ( + + ); } export { Skeleton }; diff --git a/apps/native/src/components/ui/switch.tsx b/apps/native/src/components/ui/switch.tsx index bb0fcee..91963e5 100644 --- a/apps/native/src/components/ui/switch.tsx +++ b/apps/native/src/components/ui/switch.tsx @@ -1,97 +1,107 @@ -import * as React from 'react'; -import { Platform } from 'react-native'; +import * as React from "react"; +import { Platform } from "react-native"; import Animated, { - interpolateColor, - useAnimatedStyle, - useDerivedValue, - withTiming, -} from 'react-native-reanimated'; -import * as SwitchPrimitives from '~/components/primitives/switch'; -import { useColorScheme } from '~/lib/useColorScheme'; + interpolateColor, + useAnimatedStyle, + useDerivedValue, + withTiming, +} from "react-native-reanimated"; +import * as SwitchPrimitives from "~/components/primitives/switch"; +import { useColorScheme } from "~/lib/useColorScheme"; -import { cn } from '~/lib/utils'; +import { cn } from "~/lib/utils"; const SwitchWeb = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - - - + + + )); -SwitchWeb.displayName = 'SwitchWeb'; +SwitchWeb.displayName = "SwitchWeb"; const RGB_COLORS = { - light: { - primary: 'rgb(24, 24, 27)', - input: 'rgb(228, 228, 231)', - }, - dark: { - primary: 'rgb(250, 250, 250)', - input: 'rgb(39, 39, 42)', - }, + light: { + primary: "rgb(24, 24, 27)", + input: "rgb(228, 228, 231)", + }, + dark: { + primary: "rgb(250, 250, 250)", + input: "rgb(39, 39, 42)", + }, } as const; const SwitchNative = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { colorScheme } = useColorScheme(); - const translateX = useDerivedValue(() => (props.checked ? 18 : 0)); - const animatedRootStyle = useAnimatedStyle(() => { - return { - backgroundColor: interpolateColor( - translateX.value, - [0, 18], - [RGB_COLORS[colorScheme as keyof typeof RGB_COLORS].input, RGB_COLORS[colorScheme as keyof typeof RGB_COLORS].primary] - ), - }; - }); - const animatedThumbStyle = useAnimatedStyle(() => ({ - transform: [{ translateX: withTiming(translateX.value, { duration: 200 }) }], - })); - return ( - - - - - - - - ); + const { colorScheme } = useColorScheme(); + const translateX = useDerivedValue(() => (props.checked ? 18 : 0)); + const animatedRootStyle = useAnimatedStyle(() => { + return { + backgroundColor: interpolateColor( + translateX.value, + [0, 18], + [ + RGB_COLORS[colorScheme as keyof typeof RGB_COLORS].input, + RGB_COLORS[colorScheme as keyof typeof RGB_COLORS].primary, + ], + ), + }; + }); + const animatedThumbStyle = useAnimatedStyle(() => ({ + transform: [ + { translateX: withTiming(translateX.value, { duration: 200 }) }, + ], + })); + return ( + + + + + + + + ); }); -SwitchNative.displayName = 'SwitchNative'; +SwitchNative.displayName = "SwitchNative"; const Switch = Platform.select({ - web: SwitchWeb, - default: SwitchNative, + web: SwitchWeb, + default: SwitchNative, }); export { Switch }; diff --git a/apps/native/src/components/ui/table.tsx b/apps/native/src/components/ui/table.tsx index f3848bd..a611069 100644 --- a/apps/native/src/components/ui/table.tsx +++ b/apps/native/src/components/ui/table.tsx @@ -1,99 +1,107 @@ -import * as React from 'react'; -import * as TablePrimitive from '~/components/primitives/table'; -import { cn } from '~/lib/utils'; -import { TextClassContext } from '~/components/ui/text'; +import * as React from "react"; +import * as TablePrimitive from "~/components/primitives/table"; +import { cn } from "~/lib/utils"; +import { TextClassContext } from "~/components/ui/text"; const Table = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); -Table.displayName = 'Table'; +Table.displayName = "Table"; const TableHeader = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); -TableHeader.displayName = 'TableHeader'; +TableHeader.displayName = "TableHeader"; const TableBody = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, style, ...props }, ref) => ( - + )); -TableBody.displayName = 'TableBody'; +TableBody.displayName = "TableBody"; const TableFooter = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - tr]:last:border-b-0', className)} - {...props} - /> + tr]:last:border-b-0", className)} + {...props} + /> )); -TableFooter.displayName = 'TableFooter'; +TableFooter.displayName = "TableFooter"; const TableRow = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); -TableRow.displayName = 'TableRow'; +TableRow.displayName = "TableRow"; const TableHead = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - - - + + + )); -TableHead.displayName = 'TableHead'; +TableHead.displayName = "TableHead"; const TableCell = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); -TableCell.displayName = 'TableCell'; +TableCell.displayName = "TableCell"; -export { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow }; +export { + Table, + TableBody, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +}; diff --git a/apps/native/src/components/ui/tabs.tsx b/apps/native/src/components/ui/tabs.tsx index 117912a..9923737 100644 --- a/apps/native/src/components/ui/tabs.tsx +++ b/apps/native/src/components/ui/tabs.tsx @@ -1,64 +1,65 @@ -import * as React from 'react'; -import { TextClassContext } from '~/components/ui/text'; -import * as TabsPrimitive from '~/components/primitives/tabs'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import { TextClassContext } from "~/components/ui/text"; +import * as TabsPrimitive from "~/components/primitives/tabs"; +import { cn } from "~/lib/utils"; const Tabs = TabsPrimitive.Root; const TabsList = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); TabsList.displayName = TabsPrimitive.List.displayName; const TabsTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { value } = TabsPrimitive.useRootContext(); - return ( - - - - ); + const { value } = TabsPrimitive.useRootContext(); + return ( + + + + ); }); TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; const TabsContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); TabsContent.displayName = TabsPrimitive.Content.displayName; diff --git a/apps/native/src/components/ui/text.tsx b/apps/native/src/components/ui/text.tsx index 328b49c..67d0c43 100644 --- a/apps/native/src/components/ui/text.tsx +++ b/apps/native/src/components/ui/text.tsx @@ -1,24 +1,28 @@ -import * as Slot from '~/components/primitives/slot'; -import { SlottableTextProps, TextRef } from '~/components/primitives/types'; -import * as React from 'react'; -import { Text as RNText } from 'react-native'; -import { cn } from '~/lib/utils'; +import * as Slot from "~/components/primitives/slot"; +import { SlottableTextProps, TextRef } from "~/components/primitives/types"; +import * as React from "react"; +import { Text as RNText } from "react-native"; +import { cn } from "~/lib/utils"; const TextClassContext = React.createContext(undefined); const Text = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const textClass = React.useContext(TextClassContext); - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const textClass = React.useContext(TextClassContext); + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -Text.displayName = 'Text'; +Text.displayName = "Text"; export { Text, TextClassContext }; diff --git a/apps/native/src/components/ui/textarea.tsx b/apps/native/src/components/ui/textarea.tsx index ae23352..739ac19 100644 --- a/apps/native/src/components/ui/textarea.tsx +++ b/apps/native/src/components/ui/textarea.tsx @@ -1,28 +1,39 @@ -import * as React from 'react'; -import { TextInput } from 'react-native'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import { TextInput } from "react-native"; +import { cn } from "~/lib/utils"; const Textarea = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, multiline = true, numberOfLines = 4, placeholderClassName, ...props }, ref) => { - return ( - - ); -}); + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { + className, + multiline = true, + numberOfLines = 4, + placeholderClassName, + ...props + }, + ref, + ) => { + return ( + + ); + }, +); -Textarea.displayName = 'Textarea'; +Textarea.displayName = "Textarea"; export { Textarea }; diff --git a/apps/native/src/components/ui/toggle-group.tsx b/apps/native/src/components/ui/toggle-group.tsx index 34eab16..0931a85 100644 --- a/apps/native/src/components/ui/toggle-group.tsx +++ b/apps/native/src/components/ui/toggle-group.tsx @@ -1,86 +1,91 @@ -import { VariantProps } from 'class-variance-authority'; -import type { LucideIcon } from '~/components/Icons'; -import * as React from 'react'; -import { toggleTextVariants, toggleVariants } from '~/components/ui/toggle'; -import { TextClassContext } from '~/components/ui/text'; -import * as ToggleGroupPrimitive from '~/components/primitives/toggle-group'; -import { cn } from '~/lib/utils'; +import { VariantProps } from "class-variance-authority"; +import type { LucideIcon } from "~/components/Icons"; +import * as React from "react"; +import { toggleTextVariants, toggleVariants } from "~/components/ui/toggle"; +import { TextClassContext } from "~/components/ui/text"; +import * as ToggleGroupPrimitive from "~/components/primitives/toggle-group"; +import { cn } from "~/lib/utils"; -const ToggleGroupContext = React.createContext | null>(null); +const ToggleGroupContext = React.createContext | null>(null); const ToggleGroup = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps >(({ className, variant, size, children, ...props }, ref) => ( - - {children} - + + + {children} + + )); ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName; function useToggleGroupContext() { - const context = React.useContext(ToggleGroupContext); - if (context === null) { - throw new Error( - 'ToggleGroup compound components cannot be rendered outside the ToggleGroup component' - ); - } - return context; + const context = React.useContext(ToggleGroupContext); + if (context === null) { + throw new Error( + "ToggleGroup compound components cannot be rendered outside the ToggleGroup component", + ); + } + return context; } const ToggleGroupItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps >(({ className, children, variant, size, ...props }, ref) => { - const context = useToggleGroupContext(); - const { value } = ToggleGroupPrimitive.useRootContext(); + const context = useToggleGroupContext(); + const { value } = ToggleGroupPrimitive.useRootContext(); - return ( - - - {children} - - - ); + return ( + + + {children} + + + ); }); ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName; function ToggleGroupIcon({ - className, - icon: Icon, - ...props + className, + icon: Icon, + ...props }: React.ComponentPropsWithoutRef & { - icon: LucideIcon; + icon: LucideIcon; }) { - const textClass = React.useContext(TextClassContext); - return ; + const textClass = React.useContext(TextClassContext); + return ; } export { ToggleGroup, ToggleGroupIcon, ToggleGroupItem }; diff --git a/apps/native/src/components/ui/toggle.tsx b/apps/native/src/components/ui/toggle.tsx index 69646fc..c64510e 100644 --- a/apps/native/src/components/ui/toggle.tsx +++ b/apps/native/src/components/ui/toggle.tsx @@ -1,85 +1,92 @@ -import { cva, type VariantProps } from 'class-variance-authority'; -import type { LucideIcon } from '~/components/Icons'; -import * as React from 'react'; -import { TextClassContext } from '~/components/ui/text'; -import * as TogglePrimitive from '~/components/primitives/toggle'; -import { cn } from '~/lib/utils'; +import { cva, type VariantProps } from "class-variance-authority"; +import type { LucideIcon } from "~/components/Icons"; +import * as React from "react"; +import { TextClassContext } from "~/components/ui/text"; +import * as TogglePrimitive from "~/components/primitives/toggle"; +import { cn } from "~/lib/utils"; const toggleVariants = cva( - 'web:group web:inline-flex items-center justify-center rounded-md web:ring-offset-background web:transition-colors web:hover:bg-muted active:bg-muted web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2', - { - variants: { - variant: { - default: 'bg-transparent', - outline: - 'border border-input bg-transparent web:hover:bg-accent active:bg-accent active:bg-accent', - }, - size: { - default: 'h-10 px-3 native:h-12 native:px-[12]', - sm: 'h-9 px-2.5 native:h-10 native:px-[9]', - lg: 'h-11 px-5 native:h-14 native:px-6', - }, - }, - defaultVariants: { - variant: 'default', - size: 'default', - }, - } + "web:group web:inline-flex items-center justify-center rounded-md web:ring-offset-background web:transition-colors web:hover:bg-muted active:bg-muted web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2", + { + variants: { + variant: { + default: "bg-transparent", + outline: + "border border-input bg-transparent web:hover:bg-accent active:bg-accent active:bg-accent", + }, + size: { + default: "h-10 px-3 native:h-12 native:px-[12]", + sm: "h-9 px-2.5 native:h-10 native:px-[9]", + lg: "h-11 px-5 native:h-14 native:px-6", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, ); -const toggleTextVariants = cva('text-sm native:text-base text-foreground font-medium', { - variants: { - variant: { - default: '', - outline: 'web:group-hover:text-accent-foreground web:group-active:text-accent-foreground', - }, - size: { - default: '', - sm: '', - lg: '', - }, - }, - defaultVariants: { - variant: 'default', - size: 'default', - }, -}); +const toggleTextVariants = cva( + "text-sm native:text-base text-foreground font-medium", + { + variants: { + variant: { + default: "", + outline: + "web:group-hover:text-accent-foreground web:group-active:text-accent-foreground", + }, + size: { + default: "", + sm: "", + lg: "", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); const Toggle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & VariantProps + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps >(({ className, variant, size, ...props }, ref) => ( - - - + + + )); Toggle.displayName = TogglePrimitive.Root.displayName; function ToggleIcon({ - className, - icon: Icon, - ...props + className, + icon: Icon, + ...props }: React.ComponentPropsWithoutRef & { - icon: LucideIcon; + icon: LucideIcon; }) { - const textClass = React.useContext(TextClassContext); - return ; + const textClass = React.useContext(TextClassContext); + return ; } export { Toggle, ToggleIcon, toggleTextVariants, toggleVariants }; diff --git a/apps/native/src/components/ui/tooltip.tsx b/apps/native/src/components/ui/tooltip.tsx index 99d025a..ccbdc6c 100644 --- a/apps/native/src/components/ui/tooltip.tsx +++ b/apps/native/src/components/ui/tooltip.tsx @@ -1,35 +1,37 @@ -import * as React from 'react'; -import { Platform, StyleSheet } from 'react-native'; -import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; -import { TextClassContext } from '~/components/ui/text'; -import * as TooltipPrimitive from '~/components/primitives/tooltip'; -import { cn } from '~/lib/utils'; +import * as React from "react"; +import { Platform, StyleSheet } from "react-native"; +import Animated, { FadeIn, FadeOut } from "react-native-reanimated"; +import { TextClassContext } from "~/components/ui/text"; +import * as TooltipPrimitive from "~/components/primitives/tooltip"; +import { cn } from "~/lib/utils"; const Tooltip = TooltipPrimitive.Root; const TooltipTrigger = TooltipPrimitive.Trigger; const TooltipContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, sideOffset = 4, ...props }, ref) => ( - - - - - - - - - + + + + + + + + + )); TooltipContent.displayName = TooltipPrimitive.Content.displayName; diff --git a/apps/native/src/components/ui/typography.tsx b/apps/native/src/components/ui/typography.tsx index 916b27c..1a0f3e4 100644 --- a/apps/native/src/components/ui/typography.tsx +++ b/apps/native/src/components/ui/typography.tsx @@ -1,204 +1,213 @@ -import * as Slot from '~/components/primitives/slot'; -import { SlottableTextProps, TextRef } from '~/components/primitives/types'; -import * as React from 'react'; -import { Platform, Text as RNText } from 'react-native'; -import { cn } from '~/lib/utils'; +import * as Slot from "~/components/primitives/slot"; +import { SlottableTextProps, TextRef } from "~/components/primitives/types"; +import * as React from "react"; +import { Platform, Text as RNText } from "react-native"; +import { cn } from "~/lib/utils"; const H1 = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -H1.displayName = 'H1'; +H1.displayName = "H1"; const H2 = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -H2.displayName = 'H2'; +H2.displayName = "H2"; const H3 = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -H3.displayName = 'H3'; +H3.displayName = "H3"; const H4 = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -H4.displayName = 'H4'; +H4.displayName = "H4"; const P = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -P.displayName = 'P'; +P.displayName = "P"; const BlockQuote = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -BlockQuote.displayName = 'BlockQuote'; +BlockQuote.displayName = "BlockQuote"; const Code = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -Code.displayName = 'Code'; +Code.displayName = "Code"; const Lead = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -Lead.displayName = 'Lead'; +Lead.displayName = "Lead"; const Large = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -Large.displayName = 'Large'; +Large.displayName = "Large"; const Small = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -Small.displayName = 'Small'; +Small.displayName = "Small"; const Muted = React.forwardRef( - ({ className, asChild = false, ...props }, ref) => { - const Component = asChild ? Slot.Text : RNText; - return ( - - ); - } + ({ className, asChild = false, ...props }, ref) => { + const Component = asChild ? Slot.Text : RNText; + return ( + + ); + }, ); -Muted.displayName = 'Muted'; +Muted.displayName = "Muted"; export { BlockQuote, Code, H1, H2, H3, H4, Large, Lead, Muted, P, Small }; diff --git a/apps/native/src/lib/useColorScheme.tsx b/apps/native/src/lib/useColorScheme.tsx index cee5fbf..1d3bfc3 100644 --- a/apps/native/src/lib/useColorScheme.tsx +++ b/apps/native/src/lib/useColorScheme.tsx @@ -1,11 +1,12 @@ -import { useColorScheme as useNativewindColorScheme } from 'nativewind'; +import { useColorScheme as useNativewindColorScheme } from "nativewind"; export function useColorScheme() { - const { colorScheme, setColorScheme, toggleColorScheme } = useNativewindColorScheme(); - return { - colorScheme: colorScheme ?? 'dark', - isDarkColorScheme: colorScheme === 'dark', - setColorScheme, - toggleColorScheme, - }; -} \ No newline at end of file + const { colorScheme, setColorScheme, toggleColorScheme } = + useNativewindColorScheme(); + return { + colorScheme: colorScheme ?? "dark", + isDarkColorScheme: colorScheme === "dark", + setColorScheme, + toggleColorScheme, + }; +} diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index d1bf0b6..2955240 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -17,9 +17,9 @@ }, "include": [ ".", - ".next/types/**/*.ts" + // ".next/types/**/*.ts" ], "exclude": [ "node_modules" ] -} +} \ No newline at end of file