diff --git a/.vscode/settings.json b/.vscode/settings.json index 8631b58..f702703 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,28 +1,26 @@ { - "editor.tabSize": 2, - "editor.insertSpaces": true, - "files.insertFinalNewline": true, - "files.trimTrailingWhitespace": true, - "editor.codeActionsOnSave": { - "source.fixAll": "always" - }, - "editor.defaultFormatter": "biomejs.biome", - "editor.formatOnSave": true, - "typescript.enablePromptUseWorkspaceTsdk": true, - "typescript.tsdk": "node_modules/typescript/lib", - "[typescriptreact]": { - "editor.defaultFormatter": "vscode.typescript-language-features" - }, - "[typescript]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[javascript]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "css.customData": [ - ".vscode/css_custom_data.json" - ], - "files.associations": { - "*.css": "tailwindcss" - } + "editor.tabSize": 2, + "editor.insertSpaces": true, + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "editor.codeActionsOnSave": { + "source.fixAll": "always" + }, + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + "typescript.enablePromptUseWorkspaceTsdk": true, + "typescript.tsdk": "node_modules/typescript/lib", + "[typescriptreact]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "css.customData": [".vscode/css_custom_data.json"], + "files.associations": { + "*.css": "tailwindcss" } +} diff --git a/app.config.ts b/app.config.ts new file mode 100644 index 0000000..aa8e629 --- /dev/null +++ b/app.config.ts @@ -0,0 +1,56 @@ +import type { ConfigContext, ExpoConfig } from "@expo/config"; + +export default ({ config }: ConfigContext): ExpoConfig => ({ + ...config, + name: "Launchtrack Starter", + slug: "launchtrack-starter", + version: "1.0.0", + orientation: "portrait", + icon: "./assets/images/icon.png", + scheme: "ltstarter", + userInterfaceStyle: "dark", + runtimeVersion: { + policy: "appVersion" + }, + splash: { + image: "./assets/images/splash.png", + resizeMode: "contain", + backgroundColor: "#ffffff" + }, + assetBundlePatterns: [ + "**/*" + ], + ios: { + supportsTablet: true, + bundleIdentifier: "dev.launchtrack.base" + }, + android: { + adaptiveIcon: { + foregroundImage: "./assets/images/adaptive-icon.png", + backgroundColor: "#ffffff" + }, + package: "dev.launchtrack.starterbase" + }, + web: { + bundler: "metro", + output: "single", + favicon: "./assets/images/favicon.png" + }, + plugins: [ + [ + "expo-router", + { + origin: process.env.NODE_ENV === "production" ? "https://launchtrack.github.io/expo-starter/": null + } + ] + ], + experiments: { + typedRoutes: true + }, + extra: { + eas: { + projectId: "" + } + }, + owner: "*" +}); diff --git a/app.json b/app.json deleted file mode 100644 index 6a9b107..0000000 --- a/app.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "expo": { - "name": "Launchtrack Base", - "slug": "launchtrack-base", - "version": "1.0.0", - "orientation": "portrait", - "icon": "./assets/images/icon.png", - "scheme": "myapp", - "userInterfaceStyle": "dark", - "runtimeVersion": { - "policy": "appVersion" - }, - "splash": { - "image": "./assets/images/splash.png", - "resizeMode": "contain", - "backgroundColor": "#ffffff" - }, - "assetBundlePatterns": [ - "**/*" - ], - "ios": { - "supportsTablet": true, - "bundleIdentifier": "dev.launchtrack.base" - }, - "android": { - "adaptiveIcon": { - "foregroundImage": "./assets/images/adaptive-icon.png", - "backgroundColor": "#ffffff" - }, - "package": "dev.launchtrack.starterbase" - }, - "web": { - "bundler": "metro", - "output": "single", - "favicon": "./assets/images/favicon.png" - }, - "plugins": [ - "expo-router" - ], - "experiments": { - "typedRoutes": true - }, - "extra": { - "router": { - "origin": "https://launchtrack.github.io/expo-starter/" - }, - "eas": { - "projectId": "" - } - }, - "owner": "*" - } -} diff --git a/app/+not-found.tsx b/app/+not-found.tsx index 08c97b6..41e3fad 100644 --- a/app/+not-found.tsx +++ b/app/+not-found.tsx @@ -1,15 +1,15 @@ -import { Link, Stack } from 'expo-router'; -import { View } from 'react-native'; -import { Text } from '~/components/ui/text'; +import { Link, Stack } from "expo-router"; +import { View } from "react-native"; +import { Text } from "~/components/ui/text"; export default function NotFoundScreen() { return ( <> - + This screen doesn't exist. - + Go to home screen! diff --git a/app/_layout.tsx b/app/_layout.tsx index 1258e31..c643cef 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -75,9 +75,7 @@ export default function RootLayout() { - + diff --git a/app/habits/[id].tsx b/app/habits/[id].tsx index 072cd7d..5886e85 100644 --- a/app/habits/[id].tsx +++ b/app/habits/[id].tsx @@ -30,9 +30,8 @@ import { Text } from "~/components/ui/text"; import { getHabits, setHabits } from "~/lib/storage"; import { cn } from "~/lib/utils"; - const HabitCategories = [ - { value: "tom@cruise.com", label: "Health And Wellness", }, + { value: "tom@cruise.com", label: "Health And Wellness" }, { value: "napoleon@dynamite.com", label: "Personal Development" }, { value: "kunfu@panda.com", label: "Social And Relationships" }, { value: "bruce@lee.com", label: "Productivity" }, @@ -42,11 +41,11 @@ const HabitCategories = [ { value: "lara@croft.com", label: "Leisure" }, ]; -const HabitDurations = [ - {value: 5, label: "5 minutes"}, - {value: 10, label: "10 minutes"}, - {value: 15, label: "15 minutes"}, - {value: 30, label: "30 minutes"} +const HabitDurations = [ + { value: 5, label: "5 minutes" }, + { value: 10, label: "10 minutes" }, + { value: 15, label: "15 minutes" }, + { value: 30, label: "30 minutes" }, ]; const formSchema = z.object({ @@ -60,7 +59,7 @@ const formSchema = z.object({ { value: z.string(), label: z.string() }, { invalid_type_error: "Please select a favorite email.", - } + }, ), duration: z.number().int().positive(), enableNotifications: z.boolean(), @@ -91,16 +90,16 @@ export default function FormScreen() { }; async function onSubmit(values: z.infer) { - - const oldHabits = await getHabits(); - setHabits([...oldHabits, { - ...values, - id: Math.random().toString(36).substring(7), - category: values.category.value, - }]); - router.replace('/'); - + setHabits([ + ...oldHabits, + { + ...values, + id: Math.random().toString(36).substring(7), + category: values.category.value, + }, + ]); + router.replace("/"); } return ( @@ -111,7 +110,7 @@ export default function FormScreen() { automaticallyAdjustContentInsets={false} contentInset={{ top: 12 }} > - - ( @@ -162,7 +161,7 @@ export default function FormScreen() { @@ -229,7 +228,6 @@ export default function FormScreen() { }} /> - )} /> - + @@ -260,4 +258,3 @@ export default function FormScreen() { ); } - diff --git a/app/index.tsx b/app/index.tsx index e2e5dee..f5d5abc 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -31,16 +31,20 @@ const HabitCard: React.FC = ({ habit, onDelete }) => { return ( - {habit.name} Morning + + {habit.name} + + {" "} + Morning + + {habit.description} - - - + @@ -78,23 +82,21 @@ export default function Screen() { ( - - ), + headerRight: () => , }} /> } + ItemSeparatorComponent={() => } data={habits} renderItem={({ item }) => ( handleDeleteHabit(item.id)} /> )} keyExtractor={(item) => item.id} - ListFooterComponent={} + ListFooterComponent={} /> + )} - ); -}; \ No newline at end of file +}; diff --git a/components/habit/form.tsx b/components/habit/form.tsx index 76408c2..d805c26 100644 --- a/components/habit/form.tsx +++ b/components/habit/form.tsx @@ -1,17 +1,21 @@ -import React, { useState } from 'react'; -import { View, TextInput } from 'react-native'; -import { Button } from '~/components/ui/button'; -import { Habit } from './storage'; - +import React, { useState } from "react"; +import { TextInput, View } from "react-native"; +import { Button } from "~/components/ui/button"; +import { Habit } from "./storage"; interface HabitFormProps { onSubmit: (habit: Habit) => void; initialHabit?: Habit; // Optional for updating habits } -export const HabitForm: React.FC = ({ onSubmit, initialHabit }) => { - const [name, setName] = useState(initialHabit?.name || ''); - const [completedDays, setCompletedDays] = useState(initialHabit?.completedDays || []); +export const HabitForm: React.FC = ({ + onSubmit, + initialHabit, +}) => { + const [name, setName] = useState(initialHabit?.name || ""); + const [completedDays, setCompletedDays] = useState( + initialHabit?.completedDays || [], + ); const handleSubmit = () => { const newHabit: Habit = { @@ -20,7 +24,7 @@ export const HabitForm: React.FC = ({ onSubmit, initialHabit }) completedDays, }; onSubmit(newHabit); - setName(''); // Reset form after submission + setName(""); // Reset form after submission setCompletedDays([]); }; @@ -44,16 +48,17 @@ export const HabitForm: React.FC = ({ onSubmit, initialHabit }) setCompletedDays([...completedDays, day]); } }} - className={`mr-2 mb-2 px-2 py-1 rounded-md ${completedDays.includes(day) ? 'green.500' : 'gray.300'}`} + className={`mr-2 mb-2 px-2 py-1 rounded-md ${ + completedDays.includes(day) ? "green.500" : "gray.300" + }`} > - {['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][day]} - + {["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][day]} + ))} - + ); }; diff --git a/components/habit/index.ts b/components/habit/index.ts index e236ba9..3177e56 100644 --- a/components/habit/index.ts +++ b/components/habit/index.ts @@ -1,3 +1,3 @@ -export * from "./card" -export * from "./list" -export * from "./form" \ No newline at end of file +export * from "./card"; +export * from "./list"; +export * from "./form"; diff --git a/components/header-avatar.tsx b/components/header-avatar.tsx index c91ba6d..4ea00c9 100644 --- a/components/header-avatar.tsx +++ b/components/header-avatar.tsx @@ -1,8 +1,6 @@ - - - - - - RS - - \ No newline at end of file + + + + RS + +; diff --git a/components/primitives/avatar/index.tsx b/components/primitives/avatar/index.tsx index 41a3892..a6238f6 100644 --- a/components/primitives/avatar/index.tsx +++ b/components/primitives/avatar/index.tsx @@ -1,16 +1,20 @@ -import * as React from 'react'; +import * as React from "react"; import { + Image as RNImage, 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'; +} 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'; +type AvatarState = "loading" | "error" | "loaded"; interface IRootContext extends AvatarRootProps { status: AvatarState; @@ -21,75 +25,93 @@ const RootContext = React.createContext(null); const Root = React.forwardRef( ({ asChild, alt, ...viewProps }, ref) => { - const [status, setStatus] = React.useState('loading'); + 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'); + throw new Error( + "Avatar compound components cannot be rendered outside the Avatar component", + ); } return context; } const Image = React.forwardRef< React.ElementRef, - Omit, 'alt'> & AvatarImageProps + Omit, "alt"> & AvatarImageProps >( ( - { asChild, onLoad: onLoadProps, onError: onErrorProps, onLoadingStatusChange, ...props }, - ref + { + asChild, + onLoad: onLoadProps, + onError: onErrorProps, + onLoadingStatusChange, + ...props + }, + ref, ) => { const { alt, setStatus, status } = useRootContext(); const onLoad = React.useCallback( (e: NativeSyntheticEvent) => { - setStatus('loaded'); - onLoadingStatusChange?.('loaded'); + setStatus("loaded"); + onLoadingStatusChange?.("loaded"); onLoadProps?.(e); }, - [onLoadProps] + [onLoadProps], ); const onError = React.useCallback( (e: NativeSyntheticEvent) => { - setStatus('error'); - onLoadingStatusChange?.('error'); + setStatus("error"); + onLoadingStatusChange?.("error"); onErrorProps?.(e); }, - [onErrorProps] + [onErrorProps], ); - if (status === 'error') { + if (status === "error") { return null; } const Component = asChild ? Slot.Image : RNImage; - return ; - } + 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/components/primitives/avatar/types.ts b/components/primitives/avatar/types.ts index 4d6d9c0..2a1c3b4 100644 --- a/components/primitives/avatar/types.ts +++ b/components/primitives/avatar/types.ts @@ -4,7 +4,7 @@ interface AvatarRootProps { interface AvatarImageProps { children?: React.ReactNode; - onLoadingStatusChange?: (status: 'error' | 'loaded') => void; + onLoadingStatusChange?: (status: "error" | "loaded") => void; } export type { AvatarRootProps, AvatarImageProps }; diff --git a/components/primitives/checkbox/checkbox.tsx b/components/primitives/checkbox/checkbox.tsx index 8240558..cb0fc9f 100644 --- a/components/primitives/checkbox/checkbox.tsx +++ b/components/primitives/checkbox/checkbox.tsx @@ -1,8 +1,12 @@ -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; @@ -10,8 +14,14 @@ interface RootContext extends CheckboxRootProps { const CheckboxContext = React.createContext(null); -const Root = React.forwardRef( - ({ asChild, disabled = false, checked, onCheckedChange, nativeID, ...props }, ref) => { +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' + "Checkbox compound components cannot be rendered outside the Checkbox component", ); } return context; @@ -41,7 +51,8 @@ function useCheckboxContext() { const Trigger = React.forwardRef( ({ asChild, onPress: onPressProp, ...props }, ref) => { - const { disabled, checked, onCheckedChange, nativeID } = useCheckboxContext(); + const { disabled, checked, onCheckedChange, nativeID } = + useCheckboxContext(); function onPress(ev: GestureResponderEvent) { if (disabled) return; @@ -56,7 +67,7 @@ const Trigger = React.forwardRef( ref={ref} nativeID={nativeID} aria-disabled={disabled} - role='checkbox' + role="checkbox" aria-checked={checked} onPress={onPress} accessibilityState={{ @@ -67,10 +78,10 @@ const Trigger = React.forwardRef( {...props} /> ); - } + }, ); -Trigger.displayName = 'TriggerNativeCheckbox'; +Trigger.displayName = "TriggerNativeCheckbox"; const Indicator = React.forwardRef< React.ElementRef, @@ -90,12 +101,12 @@ const Indicator = React.forwardRef< ref={ref} aria-disabled={disabled} aria-hidden={!(forceMount || checked)} - role={'presentation'} + role={"presentation"} {...props} /> ); }); -Indicator.displayName = 'IndicatorNativeCheckbox'; +Indicator.displayName = "IndicatorNativeCheckbox"; export { Indicator, Root }; diff --git a/components/primitives/checkbox/checkbox.web.tsx b/components/primitives/checkbox/checkbox.web.tsx index 661ead3..088d2ed 100644 --- a/components/primitives/checkbox/checkbox.web.tsx +++ b/components/primitives/checkbox/checkbox.web.tsx @@ -1,17 +1,32 @@ -import * as Checkbox from '@radix-ui/react-checkbox'; -import * as React from 'react'; -import { GestureResponderEvent, Pressable, View } from 'react-native'; -import { useAugmentedRef } from '~/components/primitives/hooks'; -import * as Slot from '~/components/primitives/slot'; -import type { ComponentPropsWithAsChild, PressableRef, SlottablePressableProps } from '~/components/primitives/types'; -import type { CheckboxIndicator, CheckboxRootProps } from './types'; +import * as Checkbox from "@radix-ui/react-checkbox"; +import * as React from "react"; +import { GestureResponderEvent, Pressable, View } from "react-native"; +import { useAugmentedRef } from "~/components/primitives/hooks"; +import * as Slot from "~/components/primitives/slot"; +import type { + ComponentPropsWithAsChild, + PressableRef, + SlottablePressableProps, +} from "~/components/primitives/types"; +import type { CheckboxIndicator, CheckboxRootProps } from "./types"; const CheckboxContext = React.createContext(null); -const Root = React.forwardRef( +const Root = React.forwardRef< + PressableRef, + SlottablePressableProps & CheckboxRootProps +>( ( - { asChild, disabled, checked, onCheckedChange, onPress: onPressProp, role: _role, ...props }, - ref + { + asChild, + disabled, + checked, + onCheckedChange, + onPress: onPressProp, + role: _role, + ...props + }, + ref, ) => { const augmentedRef = useAugmentedRef({ ref }); @@ -23,19 +38,19 @@ const Root = React.forwardRef { if (augmentedRef.current) { const augRef = augmentedRef.current as unknown as HTMLButtonElement; - augRef.dataset.state = checked ? 'checked' : 'unchecked'; - augRef.value = checked ? 'on' : 'off'; + augRef.dataset.state = checked ? "checked" : "unchecked"; + augRef.value = checked ? "on" : "off"; } }, [checked]); React.useLayoutEffect(() => { if (augmentedRef.current) { const augRef = augmentedRef.current as unknown as HTMLButtonElement; - augRef.type = 'button'; - augRef.role = 'checkbox'; + augRef.type = "button"; + augRef.role = "checkbox"; if (disabled) { - augRef.dataset.disabled = 'true'; + augRef.dataset.disabled = "true"; } else { augRef.dataset.disabled = undefined; } @@ -53,7 +68,7 @@ const Root = React.forwardRef ); - } + }, ); -Root.displayName = 'RootWebCheckbox'; +Root.displayName = "RootWebCheckbox"; function useCheckboxContext() { const context = React.useContext(CheckboxContext); if (context === null) { throw new Error( - 'Checkbox compound components cannot be rendered outside the Checkbox component' + "Checkbox compound components cannot be rendered outside the Checkbox component", ); } return context; @@ -86,7 +101,7 @@ const Indicator = React.forwardRef< React.useLayoutEffect(() => { if (augmentedRef.current) { const augRef = augmentedRef.current as unknown as HTMLDivElement; - augRef.dataset.state = checked ? 'checked' : 'unchecked'; + augRef.dataset.state = checked ? "checked" : "unchecked"; } }, [checked]); @@ -94,7 +109,7 @@ const Indicator = React.forwardRef< if (augmentedRef.current) { const augRef = augmentedRef.current as unknown as HTMLDivElement; if (disabled) { - augRef.dataset.disabled = 'true'; + augRef.dataset.disabled = "true"; } else { augRef.dataset.disabled = undefined; } @@ -109,6 +124,6 @@ const Indicator = React.forwardRef< ); }); -Indicator.displayName = 'IndicatorWebCheckbox'; +Indicator.displayName = "IndicatorWebCheckbox"; export { Indicator, Root }; diff --git a/components/primitives/checkbox/index.ts b/components/primitives/checkbox/index.ts index 8d78b3e..057f167 100644 --- a/components/primitives/checkbox/index.ts +++ b/components/primitives/checkbox/index.ts @@ -1 +1 @@ -export * from './checkbox'; +export * from "./checkbox"; diff --git a/components/primitives/checkbox/types.ts b/components/primitives/checkbox/types.ts index c3bf2bc..8dd5898 100644 --- a/components/primitives/checkbox/types.ts +++ b/components/primitives/checkbox/types.ts @@ -1,4 +1,4 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; interface CheckboxRootProps { checked: boolean; diff --git a/components/primitives/dialog/dialog.tsx b/components/primitives/dialog/dialog.tsx index f8e82ca..fd19176 100644 --- a/components/primitives/dialog/dialog.tsx +++ b/components/primitives/dialog/dialog.tsx @@ -1,6 +1,14 @@ -import { useControllableState } from '~/components/primitives/hooks'; -import { Portal as RNPPortal } from '~/components/primitives/portal'; -import * as Slot from '~/components/primitives/slot'; +import * as React from "react"; +import { + BackHandler, + GestureResponderEvent, + Pressable, + Text, + View, +} from "react-native"; +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, @@ -8,21 +16,30 @@ import type { SlottableViewProps, TextRef, ViewRef, -} from '~/components/primitives/types'; -import * as React from 'react'; -import { BackHandler, GestureResponderEvent, Pressable, Text, View } from 'react-native'; +} from "~/components/primitives/types"; import type { DialogContentProps, DialogOverlayProps, DialogPortalProps, DialogRootProps, RootContext, -} from './types'; +} 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) => { + ( + { + asChild, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + ...viewProps + }, + ref, + ) => { const nativeID = React.useId(); const [open = false, onOpenChange] = useControllableState({ prop: openProp, @@ -42,15 +59,17 @@ const Root = React.forwardRef( ); - } + }, ); -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'); + throw new Error( + "Dialog compound components cannot be rendered outside the Dialog component", + ); } return context; } @@ -71,16 +90,16 @@ const Trigger = React.forwardRef( ); - } + }, ); -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. @@ -101,8 +120,20 @@ function Portal({ forceMount, hostName, children }: DialogPortalProps) { ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, closeOnPress = true, onPress: OnPressProp, ...props }, ref) => { +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & DialogOverlayProps +>( + ( + { + asChild, + forceMount, + closeOnPress = true, + onPress: OnPressProp, + ...props + }, + ref, + ) => { const { open, onOpenChange } = useRootContext(); function onPress(ev: GestureResponderEvent) { @@ -120,49 +151,53 @@ const Overlay = React.forwardRef; - } + }, ); -Overlay.displayName = 'OverlayNativeDialog'; +Overlay.displayName = "OverlayNativeDialog"; -const Content = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { open, nativeID, onOpenChange } = useRootContext(); - - React.useEffect(() => { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { +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(); - }; - }, []); + return () => { + backHandler.remove(); + }; + }, []); - if (!forceMount) { - if (!open) { - return null; - } + if (!forceMount) { + if (!open) { + return null; } - - const Component = asChild ? Slot.View : View; - return ( - - ); } -); -Content.displayName = 'ContentNativeDialog'; + const Component = asChild ? Slot.View : View; + return ( + + ); +}); + +Content.displayName = "ContentNativeDialog"; const Close = React.forwardRef( ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => { @@ -179,32 +214,46 @@ const Close = React.forwardRef( ); - } + }, ); -Close.displayName = 'CloseNativeDialog'; +Close.displayName = "CloseNativeDialog"; const Title = React.forwardRef((props, ref) => { const { nativeID } = useRootContext(); - return ; + return ( + + ); }); -Title.displayName = 'TitleNativeDialog'; +Title.displayName = "TitleNativeDialog"; -const Description = React.forwardRef((props, ref) => { - const { nativeID } = useRootContext(); - return ; -}); - -Description.displayName = 'DescriptionNativeDialog'; +const Description = React.forwardRef( + (props, ref) => { + const { nativeID } = useRootContext(); + return ; + }, +); -export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext }; +Description.displayName = "DescriptionNativeDialog"; + +export { + Close, + Content, + Description, + Overlay, + Portal, + Root, + Title, + Trigger, + useRootContext, +}; function onStartShouldSetResponder() { return true; diff --git a/components/primitives/dialog/dialog.web.tsx b/components/primitives/dialog/dialog.web.tsx index 6af6b33..9b64468 100644 --- a/components/primitives/dialog/dialog.web.tsx +++ b/components/primitives/dialog/dialog.web.tsx @@ -1,6 +1,12 @@ -import * as Dialog from '@radix-ui/react-dialog'; -import { useAugmentedRef, useControllableState } from '../hooks'; -import * as Slot from '~/components/primitives/slot'; +import * as Dialog from "@radix-ui/react-dialog"; +import * as React from "react"; +import { + type GestureResponderEvent, + Pressable, + Text, + View, +} from "react-native"; +import * as Slot from "~/components/primitives/slot"; import type { PressableRef, SlottablePressableProps, @@ -8,21 +14,29 @@ import type { SlottableViewProps, TextRef, ViewRef, -} from '~/components/primitives/types'; -import * as React from 'react'; -import { Pressable, Text, View, type GestureResponderEvent } from 'react-native'; +} from "~/components/primitives/types"; +import { useAugmentedRef, useControllableState } from "../hooks"; import type { DialogContentProps, DialogOverlayProps, DialogPortalProps, DialogRootProps, RootContext, -} from './types'; +} from "./types"; const DialogContext = React.createContext(null); const Root = React.forwardRef( - ({ asChild, open: openProp, defaultOpen, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => { + ( + { + asChild, + open: openProp, + defaultOpen, + onOpenChange: onOpenChangeProp, + ...viewProps + }, + ref, + ) => { const [open = false, onOpenChange] = useControllableState({ prop: openProp, defaultProp: defaultOpen, @@ -31,20 +45,26 @@ const Root = React.forwardRef( const Component = asChild ? Slot.View : View; return ( - + ); - } + }, ); -Root.displayName = 'RootWebDialog'; +Root.displayName = "RootWebDialog"; function useRootContext() { const context = React.useContext(DialogContext); if (!context) { - throw new Error('Dialog compound components cannot be rendered outside the Dialog component'); + throw new Error( + "Dialog compound components cannot be rendered outside the Dialog component", + ); } return context; } @@ -63,8 +83,8 @@ const Trigger = React.forwardRef( React.useLayoutEffect(() => { if (augmentedRef.current) { const augRef = augmentedRef.current as unknown as HTMLButtonElement; - augRef.dataset.state = open ? 'open' : 'closed'; - augRef.type = 'button'; + augRef.dataset.state = open ? "open" : "closed"; + augRef.type = "button"; } }, [open]); @@ -74,35 +94,45 @@ const Trigger = React.forwardRef( ); - } + }, ); -Trigger.displayName = 'TriggerWebDialog'; +Trigger.displayName = "TriggerWebDialog"; function Portal({ forceMount, container, children }: DialogPortalProps) { - return ; + return ( + + ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - - - ); - } -); +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & DialogOverlayProps +>(({ asChild, forceMount, ...props }, ref) => { + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + + + ); +}); -Overlay.displayName = 'OverlayWebDialog'; +Overlay.displayName = "OverlayWebDialog"; -const Content = React.forwardRef( +const Content = React.forwardRef< + ViewRef, + SlottableViewProps & DialogContentProps +>( ( { asChild, @@ -114,7 +144,7 @@ const Content = React.forwardRef { const Component = asChild ? Slot.View : View; return ( @@ -129,10 +159,10 @@ const Content = React.forwardRef ); - } + }, ); -Content.displayName = 'ContentWebDialog'; +Content.displayName = "ContentWebDialog"; const Close = React.forwardRef( ({ asChild, onPress: onPressProp, disabled, ...props }, ref) => { @@ -149,7 +179,7 @@ const Close = React.forwardRef( React.useLayoutEffect(() => { if (augmentedRef.current) { const augRef = augmentedRef.current as unknown as HTMLButtonElement; - augRef.type = 'button'; + augRef.type = "button"; } }, []); @@ -160,38 +190,52 @@ const Close = React.forwardRef( ); - } + }, ); -Close.displayName = 'CloseWebDialog'; - -const Title = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Text : Text; - return ( - - - - ); -}); +Close.displayName = "CloseWebDialog"; -Title.displayName = 'TitleWebDialog'; +const Title = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Text : Text; + return ( + + + + ); + }, +); -const Description = React.forwardRef(({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Text : Text; - return ( - - - - ); -}); +Title.displayName = "TitleWebDialog"; -Description.displayName = 'DescriptionWebDialog'; +const Description = React.forwardRef( + ({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Text : Text; + return ( + + + + ); + }, +); -export { Close, Content, Description, Overlay, Portal, Root, Title, Trigger, useRootContext }; +Description.displayName = "DescriptionWebDialog"; + +export { + Close, + Content, + Description, + Overlay, + Portal, + Root, + Title, + Trigger, + useRootContext, +}; diff --git a/components/primitives/dialog/index.ts b/components/primitives/dialog/index.ts index 20da8e5..e5c2874 100644 --- a/components/primitives/dialog/index.ts +++ b/components/primitives/dialog/index.ts @@ -1 +1 @@ -export * from './dialog'; +export * from "./dialog"; diff --git a/components/primitives/dialog/types.ts b/components/primitives/dialog/types.ts index 439a868..da599c8 100644 --- a/components/primitives/dialog/types.ts +++ b/components/primitives/dialog/types.ts @@ -1,4 +1,4 @@ -import type { ForceMountable } from '~/components/primitives/types'; +import type { ForceMountable } from "~/components/primitives/types"; type RootContext = { open: boolean; diff --git a/components/primitives/dropdown-menu/dropdown-menu.tsx b/components/primitives/dropdown-menu/dropdown-menu.tsx index 279fbfb..dcacfa6 100644 --- a/components/primitives/dropdown-menu/dropdown-menu.tsx +++ b/components/primitives/dropdown-menu/dropdown-menu.tsx @@ -1,16 +1,19 @@ -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'; + Pressable, + Text, + View, +} from "react-native"; +import { + type LayoutPosition, + useRelativePosition, +} from "~/components/primitives/hooks"; +import { Portal as RNPPortal } from "~/components/primitives/portal"; +import * as Slot from "~/components/primitives/slot"; import type { ForceMountable, PositionedContentProps, @@ -20,7 +23,7 @@ import type { SlottableViewProps, TextRef, ViewRef, -} from '~/components/primitives/types'; +} from "~/components/primitives/types"; import type { DropdownMenuCheckboxItemProps, DropdownMenuItemProps, @@ -32,7 +35,7 @@ import type { DropdownMenuSeparatorProps, DropdownMenuSubProps, DropdownMenuSubTriggerProps, -} from './types'; +} from "./types"; interface IRootContext extends DropdownMenuRootProps { triggerPosition: LayoutPosition | null; @@ -44,38 +47,41 @@ interface IRootContext extends DropdownMenuRootProps { 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 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 ( - - - - ); - } -); + 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' + "DropdownMenu compound components cannot be rendered outside the DropdownMenu component", ); } return context; @@ -94,7 +100,7 @@ const Trigger = React.forwardRef( } return triggerRef.current; }, - [triggerRef.current] + [triggerRef.current], ); function onPress(ev: GestureResponderEvent) { @@ -112,17 +118,17 @@ const Trigger = React.forwardRef( ); - } + }, ); -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. @@ -147,9 +153,22 @@ function Portal({ forceMount, hostName, children }: DropdownMenuPortalProps) { ); } -const Overlay = React.forwardRef( - ({ asChild, forceMount, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => { - const { open, onOpenChange, setContentLayout, setTriggerPosition } = useRootContext(); +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) { @@ -168,21 +187,24 @@ const Overlay = React.forwardRef; - } + }, ); -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( +const Content = React.forwardRef< + PressableRef, + SlottablePressableProps & PositionedContentProps +>( ( { asChild = false, forceMount, - align = 'start', - side = 'bottom', + align = "start", + side = "bottom", sideOffset = 0, alignOffset = 0, avoidCollisions = true, @@ -192,7 +214,7 @@ const Content = React.forwardRef { const { open, @@ -205,12 +227,15 @@ const Content = React.forwardRef { - const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - setTriggerPosition(null); - setContentLayout(null); - onOpenChange(false); - return true; - }); + const backHandler = BackHandler.addEventListener( + "hardwareBackPress", + () => { + setTriggerPosition(null); + setContentLayout(null); + onOpenChange(false); + return true; + }, + ); return () => { setContentLayout(null); @@ -245,7 +270,7 @@ const Content = React.forwardRef ); - } + }, ); -Content.displayName = 'ContentNativeDropdownMenu'; +Content.displayName = "ContentNativeDropdownMenu"; -const Item = React.forwardRef( +const Item = React.forwardRef< + PressableRef, + SlottablePressableProps & DropdownMenuItemProps +>( ( - { asChild, textValue, onPress: onPressProp, disabled = false, closeOnPress = true, ...props }, - ref + { + asChild, + textValue, + onPress: onPressProp, + disabled = false, + closeOnPress = true, + ...props + }, + ref, ) => { - const { onOpenChange, setTriggerPosition, setContentLayout } = useRootContext(); + const { onOpenChange, setTriggerPosition, setContentLayout } = + useRootContext(); function onPress(ev: GestureResponderEvent) { if (closeOnPress) { @@ -278,7 +314,7 @@ const Item = React.forwardRef ); - } + }, ); -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 } @@ -330,9 +370,10 @@ const CheckboxItem = React.forwardRef< disabled = false, ...props }, - ref + ref, ) => { - const { onOpenChange, setContentLayout, setTriggerPosition, nativeID } = useRootContext(); + const { onOpenChange, setContentLayout, setTriggerPosition, nativeID } = + useRootContext(); function onPress(ev: GestureResponderEvent) { onCheckedChange(!checked); @@ -350,7 +391,7 @@ const CheckboxItem = React.forwardRef< ); - } + }, ); -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' + "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; @@ -409,11 +451,13 @@ const RadioItem = React.forwardRef< closeOnPress = true, ...props }, - ref + ref, ) => { - const { onOpenChange, setContentLayout, setTriggerPosition } = useRootContext(); + const { onOpenChange, setContentLayout, setTriggerPosition } = + useRootContext(); - const { value, onValueChange } = useFormItemContext() as BothFormItemContext; + const { value, onValueChange } = + useFormItemContext() as BothFormItemContext; function onPress(ev: GestureResponderEvent) { onValueChange(itemValue); if (closeOnPress) { @@ -430,7 +474,7 @@ const RadioItem = React.forwardRef< ); - } + }, ); -RadioItem.displayName = 'RadioItemNativeDropdownMenu'; +RadioItem.displayName = "RadioItemNativeDropdownMenu"; function useItemIndicatorContext() { return React.useContext(RadioItemContext); } -const ItemIndicator = React.forwardRef( - ({ asChild, forceMount, ...props }, ref) => { - const { itemValue } = useItemIndicatorContext(); - const { checked, value } = useFormItemContext() as BothFormItemContext; +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; - } + if (!forceMount) { + if (itemValue == null && !checked) { + return null; + } + if (value !== itemValue) { + return null; } - const Component = asChild ? Slot.View : View; - return ; } -); + const Component = asChild ? Slot.View : View; + return ; +}); -ItemIndicator.displayName = 'ItemIndicatorNativeDropdownMenu'; +ItemIndicator.displayName = "ItemIndicatorNativeDropdownMenu"; -const Separator = React.forwardRef( - ({ asChild, decorative, ...props }, ref) => { - const Component = asChild ? Slot.View : View; - return ; - } -); +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; @@ -486,31 +538,34 @@ const SubContext = React.createContext<{ onOpenChange: (value: boolean) => void; } | null>(null); -const Sub = React.forwardRef( - ({ asChild, open, onOpenChange, ...props }, ref) => { - const nativeID = React.useId(); +const Sub = React.forwardRef< + ViewRef, + SlottableViewProps & DropdownMenuSubProps +>(({ asChild, open, onOpenChange, ...props }, ref) => { + const nativeID = React.useId(); - const Component = asChild ? Slot.View : View; - return ( - - - - ); - } -); + 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'); + throw new Error( + "Sub compound components cannot be rendered outside of a Sub component", + ); } return context; } @@ -518,49 +573,57 @@ function useSubContext() { const SubTrigger = React.forwardRef< PressableRef, SlottablePressableProps & DropdownMenuSubTriggerProps ->(({ asChild, textValue, onPress: onPressProp, disabled = false, ...props }, ref) => { - const { nativeID, open, onOpenChange } = useSubContext(); +>( + ( + { asChild, textValue, onPress: onPressProp, disabled = false, ...props }, + ref, + ) => { + const { nativeID, open, onOpenChange } = useSubContext(); - function onPress(ev: GestureResponderEvent) { - onOpenChange(!open); - onPressProp?.(ev); - } + function onPress(ev: GestureResponderEvent) { + onOpenChange(!open); + onPressProp?.(ev); + } - const Component = asChild ? Slot.Pressable : Pressable; - return ( - - ); -}); + 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 ; } -); -Content.displayName = 'ContentNativeDropdownMenu'; + const Component = asChild ? Slot.Pressable : Pressable; + return ( + + ); +}); + +Content.displayName = "ContentNativeDropdownMenu"; export { CheckboxItem, diff --git a/components/primitives/dropdown-menu/dropdown-menu.web.tsx b/components/primitives/dropdown-menu/dropdown-menu.web.tsx index d168c24..3825a01 100644 --- a/components/primitives/dropdown-menu/dropdown-menu.web.tsx +++ b/components/primitives/dropdown-menu/dropdown-menu.web.tsx @@ -1,8 +1,8 @@ -import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; -import * as React from 'react'; -import { GestureResponderEvent, Pressable, Text, View } from 'react-native'; -import { useAugmentedRef } from '~/components/primitives/hooks'; -import * as Slot from '~/components/primitives/slot'; +import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; +import * as React from "react"; +import { GestureResponderEvent, Pressable, Text, View } from "react-native"; +import { useAugmentedRef } from "~/components/primitives/hooks"; +import * as Slot from "~/components/primitives/slot"; import type { ForceMountable, PositionedContentProps, @@ -12,8 +12,8 @@ import type { SlottableViewProps, TextRef, ViewRef, -} from '~/components/primitives/types'; -import { EmptyGestureResponderEvent } from '~/components/primitives/utils'; +} from "~/components/primitives/types"; +import { EmptyGestureResponderEvent } from "~/components/primitives/utils"; import type { DropdownMenuCheckboxItemProps, DropdownMenuItemProps, @@ -25,30 +25,33 @@ import type { DropdownMenuSeparatorProps, DropdownMenuSubProps, DropdownMenuSubTriggerProps, -} from './types'; +} from "./types"; -const DropdownMenuContext = React.createContext(null); - -const Root = React.forwardRef( - ({ asChild, open, onOpenChange, ...viewProps }, ref) => { - const Component = asChild ? Slot.View : View; - return ( - - - - - - ); - } +const DropdownMenuContext = React.createContext( + null, ); -Root.displayName = 'RootWebDropdownMenu'; +const Root = React.forwardRef< + ViewRef, + SlottableViewProps & DropdownMenuRootProps +>(({ asChild, open, onOpenChange, ...viewProps }, ref) => { + const Component = asChild ? Slot.View : View; + return ( + + + + + + ); +}); + +Root.displayName = "RootWebDropdownMenu"; function useRootContext() { const context = React.useContext(DropdownMenuContext); if (!context) { throw new Error( - 'DropdownMenu compound components cannot be rendered outside the DropdownMenu component' + "DropdownMenu compound components cannot be rendered outside the DropdownMenu component", ); } return context; @@ -62,7 +65,7 @@ const Trigger = React.forwardRef( React.useLayoutEffect(() => { if (augmentedRef.current) { const augRef = augmentedRef.current as unknown as HTMLDivElement; - augRef.dataset.state = open ? 'open' : 'closed'; + augRef.dataset.state = open ? "open" : "closed"; } }, [open]); @@ -70,7 +73,7 @@ const Trigger = React.forwardRef( if (augmentedRef.current) { const augRef = augmentedRef.current as unknown as HTMLDivElement; if (disabled) { - augRef.dataset.disabled = 'true'; + augRef.dataset.disabled = "true"; } else { augRef.dataset.disabled = undefined; } @@ -83,29 +86,39 @@ const Trigger = React.forwardRef( ); - } + }, ); -Trigger.displayName = 'TriggerWebDropdownMenu'; +Trigger.displayName = "TriggerWebDropdownMenu"; function Portal({ forceMount, container, children }: DropdownMenuPortalProps) { - return ; + return ( + + ); } -const Overlay = React.forwardRef( - ({ asChild, ...props }, ref) => { - const Component = asChild ? Slot.Pressable : Pressable; - return ; - } -); +const Overlay = React.forwardRef< + PressableRef, + SlottablePressableProps & DropdownMenuOverlayProps +>(({ asChild, ...props }, ref) => { + const Component = asChild ? Slot.Pressable : Pressable; + return ; +}); -Overlay.displayName = 'OverlayWebDropdownMenu'; +Overlay.displayName = "OverlayWebDropdownMenu"; const DropdownMenuContentContext = React.createContext<{ close: () => void; } | null>(null); -const Content = React.forwardRef( +const Content = React.forwardRef< + PressableRef, + SlottablePressableProps & PositionedContentProps +>( ( { asChild = false, @@ -127,7 +140,7 @@ const Content = React.forwardRef { const itemRef = React.useRef(null); @@ -159,7 +172,7 @@ const Content = React.forwardRef