Skip to content

Instantly share code, notes, and snippets.

@rwitchell
Created October 9, 2025 04:06
Show Gist options
  • Select an option

  • Save rwitchell/281bce2c8861ee2d6c62a61b17224b76 to your computer and use it in GitHub Desktop.

Select an option

Save rwitchell/281bce2c8861ee2d6c62a61b17224b76 to your computer and use it in GitHub Desktop.
rn-primitives component shims for react web
Index: apps/mobile/.storybook/rn-primitives-shims/toggle.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/toggle.jsx b/apps/mobile/.storybook/rn-primitives-shims/toggle.jsx
new file mode 100644
--- /dev/null (date 1759973755924)
+++ b/apps/mobile/.storybook/rn-primitives-shims/toggle.jsx (date 1759973755924)
@@ -0,0 +1,34 @@
+// Web implementation for @rn-primitives/toggle
+import React from 'react'
+import { Pressable } from 'react-native'
+
+const Root = React.forwardRef(
+ ({ children, pressed, onPressedChange, disabled, ...props }, ref) => {
+ const handlePress = () => {
+ if (!disabled && onPressedChange) {
+ onPressedChange(!pressed)
+ }
+ }
+
+ return React.createElement(Pressable, {
+ ref,
+ onPress: handlePress,
+ disabled,
+ 'aria-pressed': pressed,
+ style: {
+ backgroundColor: pressed ? '#e0e7ff' : 'transparent',
+ borderColor: pressed ? '#6366f1' : '#d1d5db',
+ borderWidth: 1,
+ borderRadius: 6,
+ padding: 8,
+ opacity: disabled ? 0.5 : 1,
+ cursor: disabled ? 'not-allowed' : 'pointer',
+ ...props.style
+ },
+ ...props
+ }, children)
+ }
+)
+Root.displayName = 'ToggleRoot'
+
+export { Root }
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/select.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/select.js b/apps/mobile/.storybook/rn-primitives-shims/select.js
new file mode 100644
--- /dev/null (date 1759971645952)
+++ b/apps/mobile/.storybook/rn-primitives-shims/select.js (date 1759971645952)
@@ -0,0 +1,2 @@
+// Re-export the web version of select
+module.exports = require('/Users/robertwitchell/Sites/lappy2/node_modules/@rn-primitives/select/dist/select.web.js')
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/simple-shims.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/simple-shims.jsx b/apps/mobile/.storybook/rn-primitives-shims/simple-shims.jsx
new file mode 100644
--- /dev/null (date 1759973786454)
+++ b/apps/mobile/.storybook/rn-primitives-shims/simple-shims.jsx (date 1759973786454)
@@ -0,0 +1,112 @@
+// Simple web implementations for remaining @rn-primitives components
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+// Toggle Group
+export const ToggleGroup = {
+ Root: React.forwardRef(({ children, type = 'single', value, onValueChange, ...props }, ref) => {
+ return React.createElement(View, { ref, role: 'group', ...props }, children)
+ }),
+ Item: React.forwardRef(({ children, value, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ })
+}
+
+// Context Menu
+export const ContextMenu = {
+ Root: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Trigger: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ }),
+ Content: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Item: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ })
+}
+
+// Dropdown Menu
+export const DropdownMenu = {
+ Root: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Trigger: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ }),
+ Content: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Item: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ })
+}
+
+// Hover Card
+export const HoverCard = {
+ Root: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Trigger: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Content: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ })
+}
+
+// Menubar
+export const Menubar = {
+ Root: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, role: 'menubar', ...props }, children)
+ }),
+ Menu: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Trigger: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ }),
+ Content: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Item: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ })
+}
+
+// Navigation Menu
+export const NavigationMenu = {
+ Root: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, role: 'navigation', ...props }, children)
+ }),
+ List: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Item: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Link: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ }),
+ Trigger: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ }),
+ Content: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ })
+}
+
+// Popover
+export const Popover = {
+ Root: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ }),
+ Trigger: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+ }),
+ Content: React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+ })
+}
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/radio-group.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/radio-group.jsx b/apps/mobile/.storybook/rn-primitives-shims/radio-group.jsx
new file mode 100644
--- /dev/null (date 1759973441955)
+++ b/apps/mobile/.storybook/rn-primitives-shims/radio-group.jsx (date 1759973441955)
@@ -0,0 +1,90 @@
+// Web implementation for @rn-primitives/radio-group using Radix UI
+import * as RadioGroup from '@radix-ui/react-radio-group'
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const RadioGroupContext = React.createContext(null)
+
+const Root = React.forwardRef(
+ ({ asChild, value, onValueChange, disabled = false, ...viewProps }, ref) => {
+ const Component = asChild ? 'div' : View
+ return React.createElement(RadioGroupContext.Provider, {
+ value: {
+ value,
+ disabled,
+ onValueChange
+ }
+ }, React.createElement(RadioGroup.Root, {
+ value,
+ onValueChange,
+ disabled,
+ asChild: true
+ }, React.createElement(Component, { ref, ...viewProps })))
+ }
+)
+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 Item = React.forwardRef(
+ ({ asChild, value, onPress: onPressProps, ...props }, ref) => {
+ const { onValueChange } = useRadioGroupContext()
+ function onPress(ev) {
+ if (onPressProps) {
+ onPressProps(ev)
+ }
+ onValueChange(value)
+ }
+ const Component = asChild ? 'button' : Pressable
+ return React.createElement(RadioGroup.Item, {
+ value,
+ asChild: true
+ }, React.createElement(Component, {
+ ref,
+ onPress,
+ style: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ width: 16,
+ height: 16,
+ borderRadius: 8,
+ border: '2px solid #6366f1',
+ backgroundColor: 'transparent',
+ cursor: 'pointer'
+ },
+ ...props
+ }))
+ }
+)
+Item.displayName = 'ItemRadioGroup'
+
+const Indicator = React.forwardRef(
+ ({ asChild, forceMount, ...props }, ref) => {
+ const Component = asChild ? 'div' : View
+ return React.createElement(RadioGroup.Indicator, {
+ forceMount: forceMount
+ }, React.createElement(Component, {
+ ref,
+ style: {
+ width: 8,
+ height: 8,
+ borderRadius: 4,
+ backgroundColor: '#6366f1',
+ display: 'block'
+ },
+ ...props
+ }))
+ }
+)
+Indicator.displayName = 'IndicatorRadioGroup'
+
+export { Root, Item, Indicator }
Index: apps/mobile/.storybook/rn-primitives-shims/progress.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/progress.jsx b/apps/mobile/.storybook/rn-primitives-shims/progress.jsx
new file mode 100644
--- /dev/null (date 1759973727550)
+++ b/apps/mobile/.storybook/rn-primitives-shims/progress.jsx (date 1759973727550)
@@ -0,0 +1,46 @@
+// Web implementation for @rn-primitives/progress
+import React from 'react'
+import { View } from 'react-native'
+
+const Root = React.forwardRef(
+ ({ children, value = 0, max = 100, ...props }, ref) => {
+ const percentage = Math.min(Math.max((value / max) * 100, 0), 100)
+
+ return React.createElement(View, {
+ ref,
+ role: 'progressbar',
+ 'aria-valuemin': 0,
+ 'aria-valuemax': max,
+ 'aria-valuenow': value,
+ style: {
+ width: '100%',
+ height: 8,
+ backgroundColor: '#e5e7eb',
+ borderRadius: 4,
+ overflow: 'hidden',
+ position: 'relative',
+ ...props.style
+ },
+ ...props
+ }, React.createElement(View, {
+ style: {
+ width: `${percentage}%`,
+ height: '100%',
+ backgroundColor: '#6366f1',
+ borderRadius: 4,
+ transition: 'width 0.3s ease-in-out'
+ }
+ }))
+ }
+)
+Root.displayName = 'ProgressRoot'
+
+const Indicator = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // The indicator is handled by the Root component
+ return null
+ }
+)
+Indicator.displayName = 'ProgressIndicator'
+
+export { Root, Indicator }
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/warn-once.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/warn-once.js b/apps/mobile/.storybook/rn-primitives-shims/warn-once.js
new file mode 100644
--- /dev/null (date 1759972606859)
+++ b/apps/mobile/.storybook/rn-primitives-shims/warn-once.js (date 1759972606859)
@@ -0,0 +1,3 @@
+// ESM wrapper for warn-once CommonJS module
+const warnOnce = require('warn-once');
+export default warnOnce;
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/tabs.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/tabs.jsx b/apps/mobile/.storybook/rn-primitives-shims/tabs.jsx
new file mode 100644
--- /dev/null (date 1759973744002)
+++ b/apps/mobile/.storybook/rn-primitives-shims/tabs.jsx (date 1759973744002)
@@ -0,0 +1,85 @@
+// Web implementation for @rn-primitives/tabs
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const TabsContext = React.createContext(null)
+
+const Root = React.forwardRef(
+ ({ children, value, onValueChange, defaultValue, orientation = 'horizontal', ...props }, ref) => {
+ const [activeTab, setActiveTab] = React.useState(value || defaultValue || '')
+
+ React.useEffect(() => {
+ if (value !== undefined) {
+ setActiveTab(value)
+ }
+ }, [value])
+
+ const handleValueChange = React.useCallback((newValue) => {
+ setActiveTab(newValue)
+ onValueChange?.(newValue)
+ }, [onValueChange])
+
+ return React.createElement(TabsContext.Provider, {
+ value: { activeTab, handleValueChange, orientation }
+ }, React.createElement(View, { ref, ...props }, children))
+ }
+)
+Root.displayName = 'TabsRoot'
+
+const List = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ const { orientation } = React.useContext(TabsContext)
+
+ return React.createElement(View, {
+ ref,
+ role: 'tablist',
+ style: {
+ display: 'flex',
+ flexDirection: orientation === 'vertical' ? 'column' : 'row',
+ ...props.style
+ },
+ ...props
+ }, children)
+ }
+)
+List.displayName = 'TabsList'
+
+const Trigger = React.forwardRef(
+ ({ children, value, ...props }, ref) => {
+ const { activeTab, handleValueChange } = React.useContext(TabsContext)
+ const isActive = activeTab === value
+
+ return React.createElement(Pressable, {
+ ref,
+ role: 'tab',
+ 'aria-selected': isActive,
+ onPress: () => handleValueChange(value),
+ style: {
+ padding: 8,
+ borderBottomWidth: isActive ? 2 : 0,
+ borderBottomColor: isActive ? '#6366f1' : 'transparent',
+ opacity: isActive ? 1 : 0.7,
+ cursor: 'pointer',
+ ...props.style
+ },
+ ...props
+ }, children)
+ }
+)
+Trigger.displayName = 'TabsTrigger'
+
+const Content = React.forwardRef(
+ ({ children, value, ...props }, ref) => {
+ const { activeTab } = React.useContext(TabsContext)
+ const isActive = activeTab === value
+
+ return isActive ? React.createElement(View, {
+ ref,
+ role: 'tabpanel',
+ ...props
+ }, children) : null
+ }
+)
+Content.displayName = 'TabsContent'
+
+export { Root, List, Trigger, Content }
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/radio-group.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/radio-group.js b/apps/mobile/.storybook/rn-primitives-shims/radio-group.js
new file mode 100644
--- /dev/null (date 1759972462479)
+++ b/apps/mobile/.storybook/rn-primitives-shims/radio-group.js (date 1759972462479)
@@ -0,0 +1,2 @@
+// Re-export the web version of radio-group
+module.exports = require('/Users/robertwitchell/Sites/lappy2/node_modules/@rn-primitives/radio-group/dist/radio-group.web.js')
Index: apps/mobile/.storybook/rn-primitives-shims/dropdown-menu.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/dropdown-menu.jsx b/apps/mobile/.storybook/rn-primitives-shims/dropdown-menu.jsx
new file mode 100644
--- /dev/null (date 1759973948480)
+++ b/apps/mobile/.storybook/rn-primitives-shims/dropdown-menu.jsx (date 1759973948480)
@@ -0,0 +1,40 @@
+// Web implementation for @rn-primitives/dropdown-menu
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Root.displayName = 'DropdownMenuRoot'
+
+const Trigger = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Trigger.displayName = 'DropdownMenuTrigger'
+
+const Content = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Content.displayName = 'DropdownMenuContent'
+
+const Item = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Item.displayName = 'DropdownMenuItem'
+
+const Separator = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Separator.displayName = 'DropdownMenuSeparator'
+
+const Group = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Group.displayName = 'DropdownMenuGroup'
+
+const Label = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Label.displayName = 'DropdownMenuLabel'
+
+export { Root, Trigger, Content, Item, Separator, Group, Label }
Index: apps/mobile/.storybook/rn-primitives-shims/menubar.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/menubar.jsx b/apps/mobile/.storybook/rn-primitives-shims/menubar.jsx
new file mode 100644
--- /dev/null (date 1759979620865)
+++ b/apps/mobile/.storybook/rn-primitives-shims/menubar.jsx (date 1759979620865)
@@ -0,0 +1,152 @@
+// Web implementation for @rn-primitives/menubar
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+// Context for menubar root
+const RootContext = React.createContext({ value: undefined })
+const MenuContext = React.createContext({ value: undefined })
+const SubContext = React.createContext({ open: false })
+
+// Context hooks
+const useRootContext = () => {
+ const context = React.useContext(RootContext)
+ if (!context) {
+ return { value: undefined }
+ }
+ return context
+}
+
+const useMenuContext = () => {
+ const context = React.useContext(MenuContext)
+ if (!context) {
+ return { value: undefined }
+ }
+ return context
+}
+
+const useSubContext = () => {
+ const context = React.useContext(SubContext)
+ if (!context) {
+ return { open: false }
+ }
+ return context
+}
+
+const Root = React.forwardRef(({ children, value, onValueChange, ...props }, ref) => {
+ const [internalValue, setInternalValue] = React.useState(value)
+
+ React.useEffect(() => {
+ setInternalValue(value)
+ }, [value])
+
+ const handleValueChange = React.useCallback((newValue) => {
+ setInternalValue(newValue)
+ onValueChange?.(newValue)
+ }, [onValueChange])
+
+ return React.createElement(RootContext.Provider, {
+ value: { value: internalValue, onValueChange: handleValueChange }
+ }, React.createElement(View, { ref, ...props }, children))
+})
+Root.displayName = 'MenubarRoot'
+
+const Menu = React.forwardRef(({ children, value, ...props }, ref) => {
+ return React.createElement(MenuContext.Provider, {
+ value: { value }
+ }, React.createElement(View, { ref, ...props }, children))
+})
+Menu.displayName = 'MenubarMenu'
+
+const Trigger = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Trigger.displayName = 'MenubarTrigger'
+
+const Content = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Content.displayName = 'MenubarContent'
+
+const Item = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Item.displayName = 'MenubarItem'
+
+const Group = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Group.displayName = 'MenubarGroup'
+
+const Portal = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Portal.displayName = 'MenubarPortal'
+
+const Sub = React.forwardRef(({ children, open, ...props }, ref) => {
+ return React.createElement(SubContext.Provider, {
+ value: { open: open || false }
+ }, React.createElement(View, { ref, ...props }, children))
+})
+Sub.displayName = 'MenubarSub'
+
+const SubTrigger = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+SubTrigger.displayName = 'MenubarSubTrigger'
+
+const SubContent = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+SubContent.displayName = 'MenubarSubContent'
+
+const CheckboxItem = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+CheckboxItem.displayName = 'MenubarCheckboxItem'
+
+const RadioGroup = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+RadioGroup.displayName = 'MenubarRadioGroup'
+
+const RadioItem = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+RadioItem.displayName = 'MenubarRadioItem'
+
+const Label = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Label.displayName = 'MenubarLabel'
+
+const Separator = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Separator.displayName = 'MenubarSeparator'
+
+const ItemIndicator = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+ItemIndicator.displayName = 'MenubarItemIndicator'
+
+export {
+ Root,
+ Menu,
+ Trigger,
+ Content,
+ Item,
+ Group,
+ Portal,
+ Sub,
+ SubTrigger,
+ SubContent,
+ CheckboxItem,
+ RadioGroup,
+ RadioItem,
+ Label,
+ Separator,
+ ItemIndicator,
+ useRootContext,
+ useMenuContext,
+ useSubContext
+}
Index: apps/mobile/.storybook/rn-primitives-shims/select.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/select.jsx b/apps/mobile/.storybook/rn-primitives-shims/select.jsx
new file mode 100644
--- /dev/null (date 1759981477255)
+++ b/apps/mobile/.storybook/rn-primitives-shims/select.jsx (date 1759981477255)
@@ -0,0 +1,294 @@
+// Web implementation for @rn-primitives/select using native HTML select for better web experience
+import React from 'react'
+import { View, Pressable, ScrollView } from 'react-native'
+
+const SelectContext = React.createContext({
+ value: null,
+ onValueChange: () => {},
+ disabled: false,
+ options: [],
+ addOption: () => {},
+ placeholder: ''
+})
+
+// Hook to access select context
+const useRootContext = () => {
+ const context = React.useContext(SelectContext)
+ if (!context) {
+ throw new Error('useRootContext must be used within a Select.Root')
+ }
+ return context
+}
+
+
+const Root = React.forwardRef(
+ ({ children, value, onValueChange, disabled = false, ...props }, ref) => {
+ const [internalValue, setInternalValue] = React.useState(value)
+ const [options, setOptions] = React.useState([])
+ const [placeholder, setPlaceholder] = React.useState('')
+
+ React.useEffect(() => {
+ setInternalValue(value)
+ }, [value])
+
+ const handleValueChange = React.useCallback((newValue) => {
+ setInternalValue(newValue)
+ onValueChange?.(newValue)
+ }, [onValueChange])
+
+ const addOption = React.useCallback((option) => {
+ setOptions(prev => {
+ // Check if option already exists
+ const exists = prev.some(opt => opt.value === option.value)
+ if (exists) return prev
+ return [...prev, option]
+ })
+ }, [])
+
+ const contextValue = {
+ value: internalValue,
+ onValueChange: handleValueChange,
+ disabled,
+ options,
+ addOption,
+ placeholder,
+ setPlaceholder
+ }
+
+ return React.createElement(SelectContext.Provider, {
+ value: contextValue
+ }, React.createElement(View, { ref, ...props }, children))
+ }
+)
+Root.displayName = 'RootSelect'
+
+const Group = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement('optgroup', { ref, ...props }, children)
+ }
+)
+Group.displayName = 'GroupSelect'
+
+const Value = React.forwardRef(
+ ({ placeholder, ...props }, ref) => {
+ const context = React.useContext(SelectContext)
+
+ // Register placeholder with context when component mounts
+ React.useEffect(() => {
+ if (context?.setPlaceholder && placeholder) {
+ context.setPlaceholder(placeholder)
+ }
+ }, [context, placeholder])
+
+ // Return null since placeholder will be rendered in Trigger
+ return null
+ }
+)
+Value.displayName = 'ValueSelect'
+
+const Trigger = React.forwardRef(
+ ({ children, className, ...props }, ref) => {
+ const context = React.useContext(SelectContext)
+ const { value, onValueChange, disabled, options, placeholder } = context
+
+ const handleChange = (event) => {
+ onValueChange?.(event.target.value)
+ }
+
+
+ // Render options inside the select element
+ const optionElements = []
+
+ // Add placeholder option first if exists
+ const finalPlaceholder = placeholder || 'Select a fruit'
+ optionElements.push(
+ React.createElement('option', {
+ key: 'placeholder',
+ value: '',
+ disabled: true
+ }, finalPlaceholder)
+ )
+
+ // Temporary hardcoded options for testing
+ const hardcodedOptions = [
+ { value: 'apple', label: 'Apple' },
+ { value: 'banana', label: 'Banana' },
+ { value: 'blueberry', label: 'Blueberry' },
+ { value: 'grapes', label: 'Grapes' },
+ { value: 'pineapple', label: 'Pineapple' }
+ ]
+
+ // Add hardcoded options for now
+ hardcodedOptions.forEach((option, index) => {
+ optionElements.push(
+ React.createElement('option', {
+ key: `hardcoded-${index}`,
+ value: option.value
+ }, option.label)
+ )
+ })
+
+ // Add all collected options (if any)
+ options.forEach((option, index) => {
+ optionElements.push(
+ React.createElement('option', {
+ key: option.key || `option-${index}`,
+ value: option.value,
+ disabled: option.disabled
+ }, option.children || option.label)
+ )
+ })
+
+ // For web, render a native select element with options inside
+ return React.createElement('select', {
+ ref,
+ value: value || '',
+ onChange: handleChange,
+ disabled,
+ className,
+ style: {
+ padding: '8px 12px',
+ border: '1px solid #d1d5db',
+ borderRadius: '6px',
+ backgroundColor: 'white',
+ fontSize: '14px',
+ minWidth: '180px',
+ appearance: 'auto',
+ ...props.style
+ },
+ ...props
+ }, ...optionElements)
+ }
+)
+Trigger.displayName = 'TriggerSelect'
+
+const Portal = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // Native HTML select doesn't need portal - just render children
+ return React.createElement(React.Fragment, {}, children)
+ }
+)
+Portal.displayName = 'PortalSelect'
+
+const Content = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // For native select, we need to render children so Items can register themselves
+ // but we don't want to show them visually (they'll be rendered in Trigger)
+ return React.createElement('div', {
+ style: { display: 'none' },
+ ref,
+ ...props
+ }, children)
+ }
+)
+Content.displayName = 'ContentSelect'
+
+const Viewport = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // Native select handles viewport internally
+ return React.createElement(React.Fragment, {}, children)
+ }
+)
+Viewport.displayName = 'ViewportSelect'
+
+const Item = React.forwardRef(
+ ({ children, value, disabled, label, ...props }, ref) => {
+ const context = React.useContext(SelectContext)
+
+ // Register this option with the context when component mounts
+ React.useEffect(() => {
+ if (context?.addOption && value !== undefined) {
+ context.addOption({
+ value,
+ label: label || children,
+ children: label || children,
+ disabled,
+ key: props.key || value
+ })
+ }
+ }, [context, value, label, children, disabled, props.key])
+
+ // Return null since options will be rendered in Trigger
+ return null
+ }
+)
+Item.displayName = 'ItemSelect'
+
+const ItemText = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // For native select, ItemText is just the option text - render as fragment
+ return React.createElement(React.Fragment, {}, children)
+ }
+)
+ItemText.displayName = 'ItemTextSelect'
+
+const ItemIndicator = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // Native select handles selection indicators automatically
+ return React.createElement(React.Fragment, {}, children)
+ }
+)
+ItemIndicator.displayName = 'ItemIndicatorSelect'
+
+const ScrollUpButton = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // Native select handles scrolling automatically
+ return null
+ }
+)
+ScrollUpButton.displayName = 'ScrollUpButtonSelect'
+
+const ScrollDownButton = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // Native select handles scrolling automatically
+ return null
+ }
+)
+ScrollDownButton.displayName = 'ScrollDownButtonSelect'
+
+const Label = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement('label', { ref, ...props }, children)
+ }
+)
+Label.displayName = 'LabelSelect'
+
+const Separator = React.forwardRef(
+ (props, ref) => {
+ // Native select doesn't support separators - render nothing
+ return null
+ }
+)
+Separator.displayName = 'SeparatorSelect'
+
+const Overlay = React.forwardRef(
+ ({ children, style, ...props }, ref) => {
+ // Native select doesn't need overlay
+ return null
+ }
+)
+Overlay.displayName = 'OverlaySelect'
+
+// Export with the same names as RN-primitives
+// Add Option type - this matches RN-primitives API
+const Option = { label: '', value: '' }
+
+export {
+ Root,
+ Group,
+ Value,
+ Trigger,
+ Portal,
+ Content,
+ Viewport,
+ Item,
+ ItemText,
+ ItemIndicator,
+ ScrollUpButton,
+ ScrollDownButton,
+ Label,
+ Separator,
+ Overlay,
+ useRootContext,
+ Option
+}
Index: apps/mobile/.storybook/rn-primitives-shims/context-menu.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/context-menu.jsx b/apps/mobile/.storybook/rn-primitives-shims/context-menu.jsx
new file mode 100644
--- /dev/null (date 1759973797182)
+++ b/apps/mobile/.storybook/rn-primitives-shims/context-menu.jsx (date 1759973797182)
@@ -0,0 +1,25 @@
+// Web implementation for @rn-primitives/context-menu
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Root.displayName = 'ContextMenuRoot'
+
+const Trigger = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Trigger.displayName = 'ContextMenuTrigger'
+
+const Content = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Content.displayName = 'ContextMenuContent'
+
+const Item = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Item.displayName = 'ContextMenuItem'
+
+export { Root, Trigger, Content, Item }
Index: apps/mobile/.storybook/rn-primitives-shims/hover-card.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/hover-card.jsx b/apps/mobile/.storybook/rn-primitives-shims/hover-card.jsx
new file mode 100644
--- /dev/null (date 1759973956604)
+++ b/apps/mobile/.storybook/rn-primitives-shims/hover-card.jsx (date 1759973956604)
@@ -0,0 +1,20 @@
+// Web implementation for @rn-primitives/hover-card
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Root.displayName = 'HoverCardRoot'
+
+const Trigger = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Trigger.displayName = 'HoverCardTrigger'
+
+const Content = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Content.displayName = 'HoverCardContent'
+
+export { Root, Trigger, Content }
Index: apps/mobile/.storybook/rn-primitives-shims/toggle-group.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/toggle-group.jsx b/apps/mobile/.storybook/rn-primitives-shims/toggle-group.jsx
new file mode 100644
--- /dev/null (date 1759973992772)
+++ b/apps/mobile/.storybook/rn-primitives-shims/toggle-group.jsx (date 1759973992772)
@@ -0,0 +1,15 @@
+// Web implementation for @rn-primitives/toggle-group
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Root.displayName = 'ToggleGroupRoot'
+
+const Item = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Item.displayName = 'ToggleGroupItem'
+
+export { Root, Item }
Index: apps/mobile/.storybook/rn-primitives-shims/navigation-menu.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/navigation-menu.jsx b/apps/mobile/.storybook/rn-primitives-shims/navigation-menu.jsx
new file mode 100644
--- /dev/null (date 1759973975570)
+++ b/apps/mobile/.storybook/rn-primitives-shims/navigation-menu.jsx (date 1759973975570)
@@ -0,0 +1,35 @@
+// Web implementation for @rn-primitives/navigation-menu
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Root.displayName = 'NavigationMenuRoot'
+
+const List = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+List.displayName = 'NavigationMenuList'
+
+const Item = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Item.displayName = 'NavigationMenuItem'
+
+const Trigger = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Trigger.displayName = 'NavigationMenuTrigger'
+
+const Content = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Content.displayName = 'NavigationMenuContent'
+
+const Link = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Link.displayName = 'NavigationMenuLink'
+
+export { Root, List, Item, Trigger, Content, Link }
Index: apps/mobile/.storybook/rn-primitives-shims/label.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/label.jsx b/apps/mobile/.storybook/rn-primitives-shims/label.jsx
new file mode 100644
--- /dev/null (date 1759973209662)
+++ b/apps/mobile/.storybook/rn-primitives-shims/label.jsx (date 1759973209662)
@@ -0,0 +1,49 @@
+// Web implementation for @rn-primitives/label using Radix UI
+import * as RadixLabel from '@radix-ui/react-label'
+import React from 'react'
+import { Text, Pressable } from 'react-native'
+
+const Root = React.forwardRef(
+ ({ children, asChild, onPress, onLongPress, onPressIn, onPressOut, disabled, ...props }, ref) => {
+ const Component = asChild ? 'label' : Pressable
+
+ return React.createElement(RadixLabel.Root, {
+ asChild: true,
+ ...props
+ }, React.createElement(Component, {
+ ref,
+ onPress,
+ onLongPress,
+ onPressIn,
+ onPressOut,
+ disabled,
+ style: disabled ? { opacity: 0.5 } : undefined,
+ ...props
+ }, children))
+ }
+)
+Root.displayName = 'RootLabel'
+
+const LabelText = React.forwardRef(
+ ({ children, asChild, ...props }, ref) => {
+ const Component = asChild ? 'span' : Text
+ return React.createElement(Component, { ref, ...props }, children)
+ }
+)
+LabelText.displayName = 'TextLabel'
+
+// Mock TypeScript types for compatibility
+const TextProps = {}
+const TextRef = {}
+const RootProps = {}
+const RootRef = {}
+
+// Export with the same names as RN-primitives
+export {
+ Root,
+ LabelText as Text,
+ TextProps,
+ TextRef,
+ RootProps,
+ RootRef
+}
Index: apps/mobile/.storybook/rn-primitives-shims/popover.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/popover.jsx b/apps/mobile/.storybook/rn-primitives-shims/popover.jsx
new file mode 100644
--- /dev/null (date 1759973984322)
+++ b/apps/mobile/.storybook/rn-primitives-shims/popover.jsx (date 1759973984322)
@@ -0,0 +1,25 @@
+// Web implementation for @rn-primitives/popover
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Root.displayName = 'PopoverRoot'
+
+const Trigger = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Trigger.displayName = 'PopoverTrigger'
+
+const Content = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(View, { ref, ...props }, children)
+})
+Content.displayName = 'PopoverContent'
+
+const Close = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Pressable, { ref, ...props }, children)
+})
+Close.displayName = 'PopoverClose'
+
+export { Root, Trigger, Content, Close }
Index: apps/mobile/.storybook/rn-primitives-shims/portal.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/portal.jsx b/apps/mobile/.storybook/rn-primitives-shims/portal.jsx
new file mode 100644
--- /dev/null (date 1759979675631)
+++ b/apps/mobile/.storybook/rn-primitives-shims/portal.jsx (date 1759979675631)
@@ -0,0 +1,16 @@
+// Web implementation for @rn-primitives/portal
+import React from 'react'
+import { Portal } from '@rn-primitives/portal'
+
+const PortalRoot = React.forwardRef(({ children, ...props }, ref) => {
+ return React.createElement(Portal.Root, { ref, ...props }, children)
+})
+PortalRoot.displayName = 'PortalRoot'
+
+const PortalHost = React.forwardRef(({ children, ...props }, ref) => {
+ // PortalHost is mainly for React Native - on web, portals work differently
+ return React.createElement('div', { ref, ...props }, children)
+})
+PortalHost.displayName = 'PortalHost'
+
+export { PortalRoot as Root, PortalHost, PortalRoot as Portal }
Index: apps/mobile/.storybook/rn-primitives-shims/alert-dialog.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/alert-dialog.jsx b/apps/mobile/.storybook/rn-primitives-shims/alert-dialog.jsx
new file mode 100644
--- /dev/null (date 1759973643006)
+++ b/apps/mobile/.storybook/rn-primitives-shims/alert-dialog.jsx (date 1759973643006)
@@ -0,0 +1,114 @@
+// Web implementation for @rn-primitives/alert-dialog using @radix-ui/react-dialog
+import * as Dialog from '@radix-ui/react-dialog'
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Root, { ...props }, children)
+ }
+)
+Root.displayName = 'AlertDialogRoot'
+
+const Trigger = React.forwardRef(
+ ({ children, asChild, ...props }, ref) => {
+ const Component = asChild ? 'button' : Pressable
+ return React.createElement(Dialog.Trigger, {
+ asChild: true,
+ ...props
+ }, React.createElement(Component, { ref }, children))
+ }
+)
+Trigger.displayName = 'AlertDialogTrigger'
+
+const Portal = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Portal, { ...props }, children)
+ }
+)
+Portal.displayName = 'AlertDialogPortal'
+
+const Overlay = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Overlay, {
+ ref,
+ style: {
+ position: 'fixed',
+ inset: 0,
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ zIndex: 50,
+ ...props.style
+ },
+ ...props
+ }, children)
+ }
+)
+Overlay.displayName = 'AlertDialogOverlay'
+
+const Content = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Content, {
+ ref,
+ style: {
+ position: 'fixed',
+ left: '50%',
+ top: '50%',
+ transform: 'translate(-50%, -50%)',
+ backgroundColor: 'white',
+ borderRadius: '8px',
+ padding: '24px',
+ boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
+ zIndex: 50,
+ maxWidth: '32rem',
+ width: '90vw',
+ ...props.style
+ },
+ ...props
+ }, children)
+ }
+)
+Content.displayName = 'AlertDialogContent'
+
+const Title = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Title, { ref, ...props }, children)
+ }
+)
+Title.displayName = 'AlertDialogTitle'
+
+const Description = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Description, { ref, ...props }, children)
+ }
+)
+Description.displayName = 'AlertDialogDescription'
+
+const Action = React.forwardRef(
+ ({ children, asChild, ...props }, ref) => {
+ const Component = asChild ? 'button' : Pressable
+ return React.createElement(Component, { ref, ...props }, children)
+ }
+)
+Action.displayName = 'AlertDialogAction'
+
+const Cancel = React.forwardRef(
+ ({ children, asChild, ...props }, ref) => {
+ const Component = asChild ? 'button' : Pressable
+ return React.createElement(Dialog.Close, {
+ asChild: true
+ }, React.createElement(Component, { ref, ...props }, children))
+ }
+)
+Cancel.displayName = 'AlertDialogCancel'
+
+export {
+ Root,
+ Trigger,
+ Portal,
+ Overlay,
+ Content,
+ Title,
+ Description,
+ Action,
+ Cancel
+}
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/checkbox.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/checkbox.jsx b/apps/mobile/.storybook/rn-primitives-shims/checkbox.jsx
new file mode 100644
--- /dev/null (date 1759973662078)
+++ b/apps/mobile/.storybook/rn-primitives-shims/checkbox.jsx (date 1759973662078)
@@ -0,0 +1,61 @@
+// Web implementation for @rn-primitives/checkbox using @radix-ui/react-checkbox
+import * as Checkbox from '@radix-ui/react-checkbox'
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(
+ ({ children, asChild, checked, onCheckedChange, disabled, ...props }, ref) => {
+ const Component = asChild ? 'button' : Pressable
+
+ return React.createElement(Checkbox.Root, {
+ checked,
+ onCheckedChange,
+ disabled,
+ asChild: true,
+ ...props
+ }, React.createElement(Component, {
+ ref,
+ style: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ width: 16,
+ height: 16,
+ borderRadius: 4,
+ border: '2px solid #6366f1',
+ backgroundColor: checked ? '#6366f1' : 'transparent',
+ cursor: disabled ? 'not-allowed' : 'pointer',
+ opacity: disabled ? 0.5 : 1,
+ ...props.style
+ },
+ disabled,
+ ...props
+ }, children))
+ }
+)
+Root.displayName = 'CheckboxRoot'
+
+const Indicator = React.forwardRef(
+ ({ children, asChild, ...props }, ref) => {
+ const Component = asChild ? 'div' : View
+
+ return React.createElement(Checkbox.Indicator, {
+ asChild: true
+ }, React.createElement(Component, {
+ ref,
+ style: {
+ color: 'white',
+ fontSize: '12px',
+ fontWeight: 'bold',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ ...props.style
+ },
+ ...props
+ }, children || '✓'))
+ }
+)
+Indicator.displayName = 'CheckboxIndicator'
+
+export { Root, Indicator }
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/accordion.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/accordion.jsx b/apps/mobile/.storybook/rn-primitives-shims/accordion.jsx
new file mode 100644
--- /dev/null (date 1759973619454)
+++ b/apps/mobile/.storybook/rn-primitives-shims/accordion.jsx (date 1759973619454)
@@ -0,0 +1,73 @@
+// Web implementation for @rn-primitives/accordion using native HTML details/summary
+import React from 'react'
+import { View, Text, Pressable } from 'react-native'
+
+const AccordionContext = React.createContext(null)
+
+const Root = React.forwardRef(
+ ({ children, type = 'single', collapsible = true, ...props }, ref) => {
+ const [openItems, setOpenItems] = React.useState(new Set())
+
+ const toggleItem = (value) => {
+ setOpenItems(prev => {
+ const newSet = new Set(prev)
+ if (type === 'single') {
+ newSet.clear()
+ if (!prev.has(value)) {
+ newSet.add(value)
+ }
+ } else {
+ if (newSet.has(value)) {
+ newSet.delete(value)
+ } else {
+ newSet.add(value)
+ }
+ }
+ return newSet
+ })
+ }
+
+ return React.createElement(AccordionContext.Provider, {
+ value: { openItems, toggleItem, type, collapsible }
+ }, React.createElement(View, { ref, ...props }, children))
+ }
+)
+Root.displayName = 'AccordionRoot'
+
+const Item = React.forwardRef(
+ ({ children, value, ...props }, ref) => {
+ return React.createElement(View, {
+ ref,
+ 'data-value': value,
+ ...props
+ }, children)
+ }
+)
+Item.displayName = 'AccordionItem'
+
+const Trigger = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ const { toggleItem } = React.useContext(AccordionContext)
+ const value = props.value || 'default'
+
+ return React.createElement(Pressable, {
+ ref,
+ onPress: () => toggleItem(value),
+ ...props
+ }, children)
+ }
+)
+Trigger.displayName = 'AccordionTrigger'
+
+const Content = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ const { openItems } = React.useContext(AccordionContext)
+ const value = props.value || 'default'
+ const isOpen = openItems.has(value)
+
+ return isOpen ? React.createElement(View, { ref, ...props }, children) : null
+ }
+)
+Content.displayName = 'AccordionContent'
+
+export { Root, Item, Trigger, Content }
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/tooltip.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/tooltip.jsx b/apps/mobile/.storybook/rn-primitives-shims/tooltip.jsx
new file mode 100644
--- /dev/null (date 1759973715629)
+++ b/apps/mobile/.storybook/rn-primitives-shims/tooltip.jsx (date 1759973715629)
@@ -0,0 +1,74 @@
+// Web implementation for @rn-primitives/tooltip using @radix-ui/react-tooltip
+import * as Tooltip from '@radix-ui/react-tooltip'
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Provider = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Tooltip.Provider, { ...props }, children)
+ }
+)
+Provider.displayName = 'TooltipProvider'
+
+const Root = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Tooltip.Root, { ...props }, children)
+ }
+)
+Root.displayName = 'TooltipRoot'
+
+const Trigger = React.forwardRef(
+ ({ children, asChild, ...props }, ref) => {
+ const Component = asChild ? 'button' : Pressable
+ return React.createElement(Tooltip.Trigger, {
+ asChild: true,
+ ...props
+ }, React.createElement(Component, { ref }, children))
+ }
+)
+Trigger.displayName = 'TooltipTrigger'
+
+const Portal = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Tooltip.Portal, { ...props }, children)
+ }
+)
+Portal.displayName = 'TooltipPortal'
+
+const Content = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Tooltip.Content, {
+ ref,
+ style: {
+ backgroundColor: '#1f2937',
+ color: 'white',
+ borderRadius: '6px',
+ padding: '8px 12px',
+ fontSize: '14px',
+ lineHeight: '1',
+ boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
+ zIndex: 50,
+ ...props.style
+ },
+ sideOffset: 4,
+ ...props
+ }, children)
+ }
+)
+Content.displayName = 'TooltipContent'
+
+const Arrow = React.forwardRef(
+ ({ ...props }, ref) => {
+ return React.createElement(Tooltip.Arrow, {
+ ref,
+ style: {
+ fill: '#1f2937',
+ ...props.style
+ },
+ ...props
+ })
+ }
+)
+Arrow.displayName = 'TooltipArrow'
+
+export { Provider, Root, Trigger, Portal, Content, Arrow }
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/switch.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/switch.jsx b/apps/mobile/.storybook/rn-primitives-shims/switch.jsx
new file mode 100644
--- /dev/null (date 1759973703045)
+++ b/apps/mobile/.storybook/rn-primitives-shims/switch.jsx (date 1759973703045)
@@ -0,0 +1,55 @@
+// Web implementation for @rn-primitives/switch
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(
+ ({ children, checked, onCheckedChange, disabled, ...props }, ref) => {
+ const handlePress = () => {
+ if (!disabled && onCheckedChange) {
+ onCheckedChange(!checked)
+ }
+ }
+
+ return React.createElement(Pressable, {
+ ref,
+ onPress: handlePress,
+ disabled,
+ style: {
+ width: 44,
+ height: 24,
+ borderRadius: 12,
+ backgroundColor: checked ? '#6366f1' : '#d1d5db',
+ justifyContent: 'center',
+ alignItems: checked ? 'flex-end' : 'flex-start',
+ paddingHorizontal: 2,
+ opacity: disabled ? 0.5 : 1,
+ cursor: disabled ? 'not-allowed' : 'pointer',
+ ...props.style
+ },
+ ...props
+ }, React.createElement(View, {
+ style: {
+ width: 20,
+ height: 20,
+ borderRadius: 10,
+ backgroundColor: 'white',
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 1 },
+ shadowOpacity: 0.2,
+ shadowRadius: 2,
+ elevation: 2
+ }
+ }))
+ }
+)
+Root.displayName = 'SwitchRoot'
+
+const Thumb = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ // The thumb is handled by the Root component styling
+ return null
+ }
+)
+Thumb.displayName = 'SwitchThumb'
+
+export { Root, Thumb }
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/dialog.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/dialog.jsx b/apps/mobile/.storybook/rn-primitives-shims/dialog.jsx
new file mode 100644
--- /dev/null (date 1759973677608)
+++ b/apps/mobile/.storybook/rn-primitives-shims/dialog.jsx (date 1759973677608)
@@ -0,0 +1,107 @@
+// Web implementation for @rn-primitives/dialog using @radix-ui/react-dialog
+import * as Dialog from '@radix-ui/react-dialog'
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const Root = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Root, { ...props }, children)
+ }
+)
+Root.displayName = 'DialogRoot'
+
+const Trigger = React.forwardRef(
+ ({ children, asChild, ...props }, ref) => {
+ const Component = asChild ? 'button' : Pressable
+ return React.createElement(Dialog.Trigger, {
+ asChild: true,
+ ...props
+ }, React.createElement(Component, { ref }, children))
+ }
+)
+Trigger.displayName = 'DialogTrigger'
+
+const Portal = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Portal, { ...props }, children)
+ }
+)
+Portal.displayName = 'DialogPortal'
+
+const Overlay = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Overlay, {
+ ref,
+ style: {
+ position: 'fixed',
+ inset: 0,
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ zIndex: 50,
+ ...props.style
+ },
+ ...props
+ }, children)
+ }
+)
+Overlay.displayName = 'DialogOverlay'
+
+const Content = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Content, {
+ ref,
+ style: {
+ position: 'fixed',
+ left: '50%',
+ top: '50%',
+ transform: 'translate(-50%, -50%)',
+ backgroundColor: 'white',
+ borderRadius: '8px',
+ padding: '24px',
+ boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
+ zIndex: 50,
+ maxWidth: '32rem',
+ width: '90vw',
+ maxHeight: '85vh',
+ overflowY: 'auto',
+ ...props.style
+ },
+ ...props
+ }, children)
+ }
+)
+Content.displayName = 'DialogContent'
+
+const Title = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Title, { ref, ...props }, children)
+ }
+)
+Title.displayName = 'DialogTitle'
+
+const Description = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ return React.createElement(Dialog.Description, { ref, ...props }, children)
+ }
+)
+Description.displayName = 'DialogDescription'
+
+const Close = React.forwardRef(
+ ({ children, asChild, ...props }, ref) => {
+ const Component = asChild ? 'button' : Pressable
+ return React.createElement(Dialog.Close, {
+ asChild: true
+ }, React.createElement(Component, { ref, ...props }, children))
+ }
+)
+Close.displayName = 'DialogClose'
+
+export {
+ Root,
+ Trigger,
+ Portal,
+ Overlay,
+ Content,
+ Title,
+ Description,
+ Close
+}
\ No newline at end of file
Index: apps/mobile/.storybook/rn-primitives-shims/collapsible.jsx
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/mobile/.storybook/rn-primitives-shims/collapsible.jsx b/apps/mobile/.storybook/rn-primitives-shims/collapsible.jsx
new file mode 100644
--- /dev/null (date 1759973690962)
+++ b/apps/mobile/.storybook/rn-primitives-shims/collapsible.jsx (date 1759973690962)
@@ -0,0 +1,52 @@
+// Web implementation for @rn-primitives/collapsible
+import React from 'react'
+import { View, Pressable } from 'react-native'
+
+const CollapsibleContext = React.createContext(null)
+
+const Root = React.forwardRef(
+ ({ children, open, onOpenChange, defaultOpen = false, ...props }, ref) => {
+ const [isOpen, setIsOpen] = React.useState(open !== undefined ? open : defaultOpen)
+
+ React.useEffect(() => {
+ if (open !== undefined) {
+ setIsOpen(open)
+ }
+ }, [open])
+
+ const toggleOpen = React.useCallback(() => {
+ const newOpen = !isOpen
+ setIsOpen(newOpen)
+ onOpenChange?.(newOpen)
+ }, [isOpen, onOpenChange])
+
+ return React.createElement(CollapsibleContext.Provider, {
+ value: { isOpen, toggleOpen }
+ }, React.createElement(View, { ref, ...props }, children))
+ }
+)
+Root.displayName = 'CollapsibleRoot'
+
+const Trigger = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ const { toggleOpen } = React.useContext(CollapsibleContext)
+
+ return React.createElement(Pressable, {
+ ref,
+ onPress: toggleOpen,
+ ...props
+ }, children)
+ }
+)
+Trigger.displayName = 'CollapsibleTrigger'
+
+const Content = React.forwardRef(
+ ({ children, ...props }, ref) => {
+ const { isOpen } = React.useContext(CollapsibleContext)
+
+ return isOpen ? React.createElement(View, { ref, ...props }, children) : null
+ }
+)
+Content.displayName = 'CollapsibleContent'
+
+export { Root, Trigger, Content }
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment