@react-native-reusables/cli 0.0.17 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/__generated/components/primitives/context-menu/context-menu.tsx +50 -28
  2. package/__generated/components/primitives/context-menu/context-menu.web.tsx +39 -10
  3. package/__generated/components/primitives/context-menu/types.ts +17 -9
  4. package/__generated/components/primitives/dropdown-menu/dropdown-menu.tsx +73 -49
  5. package/__generated/components/primitives/dropdown-menu/dropdown-menu.web.tsx +49 -20
  6. package/__generated/components/primitives/dropdown-menu/types.ts +10 -9
  7. package/__generated/components/primitives/hover-card/hover-card.tsx +40 -32
  8. package/__generated/components/primitives/hover-card/hover-card.web.tsx +28 -23
  9. package/__generated/components/primitives/hover-card/types.ts +14 -5
  10. package/__generated/components/primitives/menubar/menubar.tsx +22 -24
  11. package/__generated/components/primitives/menubar/menubar.web.tsx +10 -5
  12. package/__generated/components/primitives/menubar/types.ts +3 -2
  13. package/__generated/components/primitives/popover/popover.tsx +61 -57
  14. package/__generated/components/primitives/popover/popover.web.tsx +44 -32
  15. package/__generated/components/primitives/popover/types.ts +7 -13
  16. package/__generated/components/primitives/select/select.tsx +32 -21
  17. package/__generated/components/primitives/select/select.web.tsx +26 -13
  18. package/__generated/components/primitives/select/types.ts +8 -6
  19. package/__generated/components/primitives/tooltip/tooltip.tsx +38 -32
  20. package/__generated/components/primitives/tooltip/tooltip.web.tsx +29 -15
  21. package/__generated/components/primitives/tooltip/types.ts +8 -10
  22. package/package.json +14 -14
@@ -1,15 +1,9 @@
1
- import * as React from 'react';
2
1
  import {
3
- BackHandler,
4
- Pressable,
5
- Text,
6
- View,
7
- type AccessibilityActionEvent,
8
- type GestureResponderEvent,
9
- type LayoutChangeEvent,
10
- type LayoutRectangle,
11
- } from 'react-native';
12
- import { useRelativePosition, type LayoutPosition } from '@rnr/hooks';
2
+ useAugmentedRef,
3
+ useControllableState,
4
+ useRelativePosition,
5
+ type LayoutPosition,
6
+ } from '@rnr/hooks';
13
7
  import { Portal as RNPPortal } from '@rnr/portal';
14
8
  import * as Slot from '@rnr/slot';
15
9
  import type {
@@ -22,6 +16,17 @@ import type {
22
16
  TextRef,
23
17
  ViewRef,
24
18
  } from '@rnr/types';
19
+ import * as React from 'react';
20
+ import {
21
+ BackHandler,
22
+ Pressable,
23
+ Text,
24
+ View,
25
+ type AccessibilityActionEvent,
26
+ type GestureResponderEvent,
27
+ type LayoutChangeEvent,
28
+ type LayoutRectangle,
29
+ } from 'react-native';
25
30
  import type {
26
31
  ContextMenuCheckboxItemProps,
27
32
  ContextMenuItemProps,
@@ -33,9 +38,12 @@ import type {
33
38
  ContextMenuSeparatorProps,
34
39
  ContextMenuSubProps,
35
40
  ContextMenuSubTriggerProps,
41
+ ContextMenuTriggerRef,
36
42
  } from './types';
37
43
 
38
44
  interface IRootContext extends ContextMenuRootProps {
45
+ open: boolean;
46
+ onOpenChange: (open: boolean) => void;
39
47
  pressPosition: LayoutPosition | null;
40
48
  setPressPosition: (pressPosition: LayoutPosition | null) => void;
41
49
  contentLayout: LayoutRectangle | null;
@@ -46,10 +54,16 @@ interface IRootContext extends ContextMenuRootProps {
46
54
  const RootContext = React.createContext<IRootContext | null>(null);
47
55
 
48
56
  const Root = React.forwardRef<ViewRef, SlottableViewProps & ContextMenuRootProps>(
49
- ({ asChild, open, onOpenChange, relativeTo = 'longPress', ...viewProps }, ref) => {
57
+ ({ asChild, relativeTo = 'longPress', onOpenChange: onOpenChangeProp, ...viewProps }, ref) => {
50
58
  const nativeID = React.useId();
51
59
  const [pressPosition, setPressPosition] = React.useState<LayoutPosition | null>(null);
52
60
  const [contentLayout, setContentLayout] = React.useState<LayoutRectangle | null>(null);
61
+ const [open, setOpen] = React.useState(false);
62
+
63
+ function onOpenChange(value: boolean) {
64
+ setOpen(value);
65
+ onOpenChangeProp?.(value);
66
+ }
53
67
 
54
68
  const Component = asChild ? Slot.View : View;
55
69
  return (
@@ -85,7 +99,7 @@ function useRootContext() {
85
99
 
86
100
  const accessibilityActions = [{ name: 'longpress' }];
87
101
 
88
- const Trigger = React.forwardRef<PressableRef, SlottablePressableProps>(
102
+ const Trigger = React.forwardRef<ContextMenuTriggerRef, SlottablePressableProps>(
89
103
  (
90
104
  {
91
105
  asChild,
@@ -96,19 +110,22 @@ const Trigger = React.forwardRef<PressableRef, SlottablePressableProps>(
96
110
  },
97
111
  ref
98
112
  ) => {
99
- const triggerRef = React.useRef<View>(null);
100
113
  const { open, onOpenChange, relativeTo, setPressPosition } = useRootContext();
101
-
102
- React.useImperativeHandle(
114
+ const augmentedRef = useAugmentedRef({
103
115
  ref,
104
- () => {
105
- if (!triggerRef.current) {
106
- return new View({});
107
- }
108
- return triggerRef.current;
116
+ methods: {
117
+ open: () => {
118
+ onOpenChange(true);
119
+ augmentedRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
120
+ setPressPosition({ width, pageX, pageY: pageY, height });
121
+ });
122
+ },
123
+ close: () => {
124
+ setPressPosition(null);
125
+ onOpenChange(false);
126
+ },
109
127
  },
110
- [triggerRef.current]
111
- );
128
+ });
112
129
 
113
130
  function onLongPress(ev: GestureResponderEvent) {
114
131
  if (disabled) return;
@@ -121,7 +138,7 @@ const Trigger = React.forwardRef<PressableRef, SlottablePressableProps>(
121
138
  });
122
139
  }
123
140
  if (relativeTo === 'trigger') {
124
- triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
141
+ augmentedRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
125
142
  setPressPosition({ width, pageX, pageY: pageY, height });
126
143
  });
127
144
  }
@@ -147,7 +164,7 @@ const Trigger = React.forwardRef<PressableRef, SlottablePressableProps>(
147
164
  const Component = asChild ? Slot.Pressable : Pressable;
148
165
  return (
149
166
  <Component
150
- ref={triggerRef}
167
+ ref={augmentedRef}
151
168
  aria-disabled={disabled ?? undefined}
152
169
  role='button'
153
170
  onLongPress={onLongPress}
@@ -386,7 +403,6 @@ const CheckboxItem = React.forwardRef<
386
403
  <FormItemContext.Provider value={{ checked }}>
387
404
  <Component
388
405
  ref={ref}
389
- key={`checkbox-${nativeID}-${checked}`}
390
406
  role='checkbox'
391
407
  aria-checked={checked}
392
408
  onPress={onPress}
@@ -524,8 +540,13 @@ const SubContext = React.createContext<{
524
540
  } | null>(null);
525
541
 
526
542
  const Sub = React.forwardRef<ViewRef, SlottableViewProps & ContextMenuSubProps>(
527
- ({ asChild, open, onOpenChange, ...props }, ref) => {
543
+ ({ asChild, defaultOpen, open: openProp, onOpenChange: onOpenChangeProp, ...props }, ref) => {
528
544
  const nativeID = React.useId();
545
+ const [open = false, onOpenChange] = useControllableState({
546
+ prop: openProp,
547
+ defaultProp: defaultOpen,
548
+ onChange: onOpenChangeProp,
549
+ });
529
550
 
530
551
  const Component = asChild ? Slot.View : View;
531
552
  return (
@@ -567,7 +588,6 @@ const SubTrigger = React.forwardRef<
567
588
  return (
568
589
  <Component
569
590
  ref={ref}
570
- key={`sub-trigger-${nativeID}-${open}`}
571
591
  aria-valuetext={textValue}
572
592
  role='menuitem'
573
593
  aria-expanded={open}
@@ -621,6 +641,8 @@ export {
621
641
  useSubContext,
622
642
  };
623
643
 
644
+ export type { ContextMenuTriggerRef };
645
+
624
646
  function onStartShouldSetResponder() {
625
647
  return true;
626
648
  }
@@ -1,8 +1,6 @@
1
1
  import * as ContextMenu from '@radix-ui/react-context-menu';
2
- import * as React from 'react';
3
- import { GestureResponderEvent, Pressable, Text, View } from 'react-native';
2
+ import { useAugmentedRef, useControllableState } from '@rnr/hooks';
4
3
  import * as Slot from '@rnr/slot';
5
- import { useAugmentedRef } from '@rnr/hooks';
6
4
  import type {
7
5
  ForceMountable,
8
6
  PositionedContentProps,
@@ -13,6 +11,9 @@ import type {
13
11
  TextRef,
14
12
  ViewRef,
15
13
  } from '@rnr/types';
14
+ import { EmptyGestureResponderEvent } from '@rnr/utils';
15
+ import * as React from 'react';
16
+ import { GestureResponderEvent, Pressable, Text, View } from 'react-native';
16
17
  import type {
17
18
  ContextMenuCheckboxItemProps,
18
19
  ContextMenuItemProps,
@@ -24,13 +25,23 @@ import type {
24
25
  ContextMenuSeparatorProps,
25
26
  ContextMenuSubProps,
26
27
  ContextMenuSubTriggerProps,
28
+ ContextMenuTriggerRef,
27
29
  } from './types';
28
- import { EmptyGestureResponderEvent } from '@rnr/utils';
29
30
 
30
- const ContextMenuContext = React.createContext<ContextMenuRootProps | null>(null);
31
+ const ContextMenuContext = React.createContext<{
32
+ open: boolean;
33
+ onOpenChange: (open: boolean) => void;
34
+ } | null>(null);
31
35
 
32
36
  const Root = React.forwardRef<ViewRef, SlottableViewProps & ContextMenuRootProps>(
33
- ({ asChild, open, onOpenChange, ...viewProps }, ref) => {
37
+ ({ asChild, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => {
38
+ const [open, setOpen] = React.useState(false);
39
+
40
+ function onOpenChange(value: boolean) {
41
+ setOpen(value);
42
+ onOpenChangeProp?.(value);
43
+ }
44
+
34
45
  const Component = asChild ? Slot.View : View;
35
46
  return (
36
47
  <ContextMenuContext.Provider value={{ open, onOpenChange }}>
@@ -54,10 +65,20 @@ function useRootContext() {
54
65
  return context;
55
66
  }
56
67
 
57
- const Trigger = React.forwardRef<PressableRef, SlottablePressableProps>(
68
+ const Trigger = React.forwardRef<ContextMenuTriggerRef, SlottablePressableProps>(
58
69
  ({ asChild, disabled = false, ...props }, ref) => {
59
- const augmentedRef = useAugmentedRef({ ref });
60
70
  const { open } = useRootContext();
71
+ const augmentedRef = useAugmentedRef({
72
+ ref,
73
+ methods: {
74
+ open() {
75
+ console.warn('Warning: `open()` is only for Native platforms');
76
+ },
77
+ close() {
78
+ console.warn('Warning: `close()` is only for Native platforms');
79
+ },
80
+ },
81
+ });
61
82
 
62
83
  React.useLayoutEffect(() => {
63
84
  if (augmentedRef.current) {
@@ -425,7 +446,13 @@ const ContextMenuSubContext = React.createContext<{
425
446
  } | null>(null);
426
447
 
427
448
  const Sub = React.forwardRef<ViewRef, SlottableViewProps & ContextMenuSubProps>(
428
- ({ asChild, open, onOpenChange, ...props }, ref) => {
449
+ ({ asChild, defaultOpen, open: openProp, onOpenChange: onOpenChangeProp, ...props }, ref) => {
450
+ const [open = false, onOpenChange] = useControllableState({
451
+ prop: openProp,
452
+ defaultProp: defaultOpen,
453
+ onChange: onOpenChangeProp,
454
+ });
455
+
429
456
  const Component = asChild ? Slot.View : View;
430
457
  return (
431
458
  <ContextMenuSubContext.Provider value={{ open, onOpenChange }}>
@@ -495,10 +522,12 @@ export {
495
522
  SubContent,
496
523
  SubTrigger,
497
524
  Trigger,
498
- useSubContext,
499
525
  useRootContext,
526
+ useSubContext,
500
527
  };
501
528
 
529
+ export type { ContextMenuTriggerRef };
530
+
502
531
  function onSelected(ev: Event) {
503
532
  ev.preventDefault();
504
533
  }
@@ -1,12 +1,7 @@
1
- import { ForceMountable } from '@rnr/types';
1
+ import { ForceMountable, PressableRef } from '@rnr/types';
2
2
 
3
3
  interface ContextMenuRootProps {
4
- /**
5
- * Platform: NATIVE ONLY
6
- */
7
- open: boolean;
8
- onOpenChange: (value: boolean) => void;
9
-
4
+ onOpenChange?: (open: boolean) => void;
10
5
  /**
11
6
  * Platform: NATIVE ONLY
12
7
  */
@@ -60,14 +55,26 @@ interface ContextMenuSeparatorProps {
60
55
  }
61
56
 
62
57
  interface ContextMenuSubProps {
63
- open: boolean;
64
- onOpenChange: (value: boolean) => void;
58
+ defaultOpen?: boolean;
59
+ open?: boolean;
60
+ onOpenChange?: (value: boolean) => void;
65
61
  }
66
62
 
67
63
  interface ContextMenuSubTriggerProps {
68
64
  textValue?: string;
69
65
  }
70
66
 
67
+ interface ContextMenuTriggerRef extends PressableRef {
68
+ /**
69
+ * Platform: NATIVE ONLY
70
+ */
71
+ open: () => void;
72
+ /**
73
+ * Platform: NATIVE ONLY
74
+ */
75
+ close: () => void;
76
+ }
77
+
71
78
  export type {
72
79
  ContextMenuCheckboxItemProps,
73
80
  ContextMenuItemProps,
@@ -79,4 +86,5 @@ export type {
79
86
  ContextMenuSeparatorProps,
80
87
  ContextMenuSubProps,
81
88
  ContextMenuSubTriggerProps,
89
+ ContextMenuTriggerRef,
82
90
  };
@@ -1,14 +1,9 @@
1
- import * as React from 'react';
2
1
  import {
3
- BackHandler,
4
- Pressable,
5
- Text,
6
- View,
7
- type GestureResponderEvent,
8
- type LayoutChangeEvent,
9
- type LayoutRectangle,
10
- } from 'react-native';
11
- import { useRelativePosition, type LayoutPosition } from '@rnr/hooks';
2
+ useAugmentedRef,
3
+ useControllableState,
4
+ useRelativePosition,
5
+ type LayoutPosition,
6
+ } from '@rnr/hooks';
12
7
  import { Portal as RNPPortal } from '@rnr/portal';
13
8
  import * as Slot from '@rnr/slot';
14
9
  import type {
@@ -21,6 +16,16 @@ import type {
21
16
  TextRef,
22
17
  ViewRef,
23
18
  } from '@rnr/types';
19
+ import * as React from 'react';
20
+ import {
21
+ BackHandler,
22
+ Pressable,
23
+ Text,
24
+ View,
25
+ type GestureResponderEvent,
26
+ type LayoutChangeEvent,
27
+ type LayoutRectangle,
28
+ } from 'react-native';
24
29
  import type {
25
30
  DropdownMenuCheckboxItemProps,
26
31
  DropdownMenuItemProps,
@@ -28,13 +33,15 @@ import type {
28
33
  DropdownMenuPortalProps,
29
34
  DropdownMenuRadioGroupProps,
30
35
  DropdownMenuRadioItemProps,
31
- DropdownMenuRootProps,
32
36
  DropdownMenuSeparatorProps,
33
37
  DropdownMenuSubProps,
34
38
  DropdownMenuSubTriggerProps,
39
+ DropdownMenuTriggerRef,
35
40
  } from './types';
36
41
 
37
- interface IRootContext extends DropdownMenuRootProps {
42
+ interface IRootContext {
43
+ open: boolean;
44
+ onOpenChange: (open: boolean) => void;
38
45
  triggerPosition: LayoutPosition | null;
39
46
  setTriggerPosition: (triggerPosition: LayoutPosition | null) => void;
40
47
  contentLayout: LayoutRectangle | null;
@@ -44,30 +51,37 @@ interface IRootContext extends DropdownMenuRootProps {
44
51
 
45
52
  const RootContext = React.createContext<IRootContext | null>(null);
46
53
 
47
- const Root = React.forwardRef<ViewRef, SlottableViewProps & DropdownMenuRootProps>(
48
- ({ asChild, open, onOpenChange, ...viewProps }, ref) => {
49
- const nativeID = React.useId();
50
- const [triggerPosition, setTriggerPosition] = React.useState<LayoutPosition | null>(null);
51
- const [contentLayout, setContentLayout] = React.useState<LayoutRectangle | null>(null);
52
-
53
- const Component = asChild ? Slot.View : View;
54
- return (
55
- <RootContext.Provider
56
- value={{
57
- open,
58
- onOpenChange,
59
- contentLayout,
60
- setContentLayout,
61
- nativeID,
62
- setTriggerPosition,
63
- triggerPosition,
64
- }}
65
- >
66
- <Component ref={ref} {...viewProps} />
67
- </RootContext.Provider>
68
- );
54
+ const Root = React.forwardRef<
55
+ ViewRef,
56
+ SlottableViewProps & { onOpenChange?: (open: boolean) => void }
57
+ >(({ asChild, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => {
58
+ const nativeID = React.useId();
59
+ const [triggerPosition, setTriggerPosition] = React.useState<LayoutPosition | null>(null);
60
+ const [contentLayout, setContentLayout] = React.useState<LayoutRectangle | null>(null);
61
+ const [open, setOpen] = React.useState(false);
62
+
63
+ function onOpenChange(open: boolean) {
64
+ setOpen(open);
65
+ onOpenChangeProp?.(open);
69
66
  }
70
- );
67
+
68
+ const Component = asChild ? Slot.View : View;
69
+ return (
70
+ <RootContext.Provider
71
+ value={{
72
+ open,
73
+ onOpenChange,
74
+ contentLayout,
75
+ setContentLayout,
76
+ nativeID,
77
+ setTriggerPosition,
78
+ triggerPosition,
79
+ }}
80
+ >
81
+ <Component ref={ref} {...viewProps} />
82
+ </RootContext.Provider>
83
+ );
84
+ });
71
85
 
72
86
  Root.displayName = 'RootNativeDropdownMenu';
73
87
 
@@ -81,25 +95,29 @@ function useRootContext() {
81
95
  return context;
82
96
  }
83
97
 
84
- const Trigger = React.forwardRef<PressableRef, SlottablePressableProps>(
98
+ const Trigger = React.forwardRef<DropdownMenuTriggerRef, SlottablePressableProps>(
85
99
  ({ asChild, onPress: onPressProp, disabled = false, ...props }, ref) => {
86
- const triggerRef = React.useRef<View>(null);
87
100
  const { open, onOpenChange, setTriggerPosition } = useRootContext();
88
101
 
89
- React.useImperativeHandle(
102
+ const augmentedRef = useAugmentedRef({
90
103
  ref,
91
- () => {
92
- if (!triggerRef.current) {
93
- return new View({});
94
- }
95
- return triggerRef.current;
104
+ methods: {
105
+ open: () => {
106
+ onOpenChange(true);
107
+ augmentedRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
108
+ setTriggerPosition({ width, pageX, pageY: pageY, height });
109
+ });
110
+ },
111
+ close: () => {
112
+ setTriggerPosition(null);
113
+ onOpenChange(false);
114
+ },
96
115
  },
97
- [triggerRef.current]
98
- );
116
+ });
99
117
 
100
118
  function onPress(ev: GestureResponderEvent) {
101
119
  if (disabled) return;
102
- triggerRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
120
+ augmentedRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
103
121
  setTriggerPosition({ width, pageX, pageY: pageY, height });
104
122
  });
105
123
  const newValue = !open;
@@ -110,7 +128,7 @@ const Trigger = React.forwardRef<PressableRef, SlottablePressableProps>(
110
128
  const Component = asChild ? Slot.Pressable : Pressable;
111
129
  return (
112
130
  <Component
113
- ref={triggerRef}
131
+ ref={augmentedRef}
114
132
  aria-disabled={disabled ?? undefined}
115
133
  role='button'
116
134
  onPress={onPress}
@@ -349,7 +367,6 @@ const CheckboxItem = React.forwardRef<
349
367
  <FormItemContext.Provider value={{ checked }}>
350
368
  <Component
351
369
  ref={ref}
352
- key={`checkbox-${nativeID}-${checked}`}
353
370
  role='checkbox'
354
371
  aria-checked={checked}
355
372
  onPress={onPress}
@@ -487,8 +504,13 @@ const SubContext = React.createContext<{
487
504
  } | null>(null);
488
505
 
489
506
  const Sub = React.forwardRef<ViewRef, SlottableViewProps & DropdownMenuSubProps>(
490
- ({ asChild, open, onOpenChange, ...props }, ref) => {
507
+ ({ asChild, defaultOpen, open: openProp, onOpenChange: onOpenChangeProp, ...props }, ref) => {
491
508
  const nativeID = React.useId();
509
+ const [open = false, onOpenChange] = useControllableState({
510
+ prop: openProp,
511
+ defaultProp: defaultOpen,
512
+ onChange: onOpenChangeProp,
513
+ });
492
514
 
493
515
  const Component = asChild ? Slot.View : View;
494
516
  return (
@@ -582,3 +604,5 @@ export {
582
604
  useRootContext,
583
605
  useSubContext,
584
606
  };
607
+
608
+ export type { DropdownMenuTriggerRef };
@@ -1,7 +1,5 @@
1
1
  import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
2
- import * as React from 'react';
3
- import { GestureResponderEvent, Pressable, Text, View } from 'react-native';
4
- import { useAugmentedRef } from '@rnr/hooks';
2
+ import { useAugmentedRef, useControllableState } from '@rnr/hooks';
5
3
  import * as Slot from '@rnr/slot';
6
4
  import type {
7
5
  ForceMountable,
@@ -14,6 +12,8 @@ import type {
14
12
  ViewRef,
15
13
  } from '@rnr/types';
16
14
  import { EmptyGestureResponderEvent } from '@rnr/utils';
15
+ import * as React from 'react';
16
+ import { GestureResponderEvent, Pressable, Text, View } from 'react-native';
17
17
  import type {
18
18
  DropdownMenuCheckboxItemProps,
19
19
  DropdownMenuItemProps,
@@ -21,26 +21,37 @@ import type {
21
21
  DropdownMenuPortalProps,
22
22
  DropdownMenuRadioGroupProps,
23
23
  DropdownMenuRadioItemProps,
24
- DropdownMenuRootProps,
25
24
  DropdownMenuSeparatorProps,
26
25
  DropdownMenuSubProps,
27
26
  DropdownMenuSubTriggerProps,
27
+ DropdownMenuTriggerRef,
28
28
  } from './types';
29
29
 
30
- const DropdownMenuContext = React.createContext<DropdownMenuRootProps | null>(null);
30
+ const DropdownMenuContext = React.createContext<{
31
+ open: boolean;
32
+ onOpenChange: (open: boolean) => void;
33
+ } | null>(null);
31
34
 
32
- const Root = React.forwardRef<ViewRef, SlottableViewProps & DropdownMenuRootProps>(
33
- ({ asChild, open, onOpenChange, ...viewProps }, ref) => {
34
- const Component = asChild ? Slot.View : View;
35
- return (
36
- <DropdownMenuContext.Provider value={{ open, onOpenChange }}>
37
- <DropdownMenu.Root open={open} onOpenChange={onOpenChange}>
38
- <Component ref={ref} {...viewProps} />
39
- </DropdownMenu.Root>
40
- </DropdownMenuContext.Provider>
41
- );
35
+ const Root = React.forwardRef<
36
+ ViewRef,
37
+ SlottableViewProps & { onOpenChange?: (open: boolean) => void }
38
+ >(({ asChild, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => {
39
+ const [open, setOpen] = React.useState(false);
40
+
41
+ function onOpenChange(open: boolean) {
42
+ setOpen(open);
43
+ onOpenChangeProp?.(open);
42
44
  }
43
- );
45
+
46
+ const Component = asChild ? Slot.View : View;
47
+ return (
48
+ <DropdownMenuContext.Provider value={{ open, onOpenChange }}>
49
+ <DropdownMenu.Root open={open} onOpenChange={onOpenChange}>
50
+ <Component ref={ref} {...viewProps} />
51
+ </DropdownMenu.Root>
52
+ </DropdownMenuContext.Provider>
53
+ );
54
+ });
44
55
 
45
56
  Root.displayName = 'RootWebDropdownMenu';
46
57
 
@@ -54,10 +65,20 @@ function useRootContext() {
54
65
  return context;
55
66
  }
56
67
 
57
- const Trigger = React.forwardRef<PressableRef, SlottablePressableProps>(
68
+ const Trigger = React.forwardRef<DropdownMenuTriggerRef, SlottablePressableProps>(
58
69
  ({ asChild, disabled = false, ...props }, ref) => {
59
- const augmentedRef = useAugmentedRef({ ref });
60
- const { open } = useRootContext();
70
+ const { open, onOpenChange } = useRootContext();
71
+ const augmentedRef = useAugmentedRef({
72
+ ref,
73
+ methods: {
74
+ open() {
75
+ onOpenChange(true);
76
+ },
77
+ close() {
78
+ onOpenChange(false);
79
+ },
80
+ },
81
+ });
61
82
 
62
83
  React.useLayoutEffect(() => {
63
84
  if (augmentedRef.current) {
@@ -442,7 +463,13 @@ const DropdownMenuSubContext = React.createContext<{
442
463
  } | null>(null);
443
464
 
444
465
  const Sub = React.forwardRef<ViewRef, SlottableViewProps & DropdownMenuSubProps>(
445
- ({ asChild, open, onOpenChange, ...props }, ref) => {
466
+ ({ asChild, defaultOpen, open: openProp, onOpenChange: onOpenChangeProp, ...props }, ref) => {
467
+ const [open = false, onOpenChange] = useControllableState({
468
+ prop: openProp,
469
+ defaultProp: defaultOpen,
470
+ onChange: onOpenChangeProp,
471
+ });
472
+
446
473
  const Component = asChild ? Slot.View : View;
447
474
  return (
448
475
  <DropdownMenuSubContext.Provider value={{ open, onOpenChange }}>
@@ -516,6 +543,8 @@ export {
516
543
  useSubContext,
517
544
  };
518
545
 
546
+ export type { DropdownMenuTriggerRef };
547
+
519
548
  function onSelected(ev: Event) {
520
549
  ev.preventDefault();
521
550
  }
@@ -1,9 +1,4 @@
1
- import type { ForceMountable } from '@rnr/types';
2
-
3
- interface DropdownMenuRootProps {
4
- open: boolean;
5
- onOpenChange: (value: boolean) => void;
6
- }
1
+ import type { ForceMountable, PressableRef } from '@rnr/types';
7
2
 
8
3
  interface DropdownMenuPortalProps extends ForceMountable {
9
4
  children: React.ReactNode;
@@ -49,14 +44,20 @@ interface DropdownMenuSeparatorProps {
49
44
  }
50
45
 
51
46
  interface DropdownMenuSubProps {
52
- open: boolean;
53
- onOpenChange: (value: boolean) => void;
47
+ defaultOpen?: boolean;
48
+ open?: boolean;
49
+ onOpenChange?: (value: boolean) => void;
54
50
  }
55
51
 
56
52
  interface DropdownMenuSubTriggerProps {
57
53
  textValue?: string;
58
54
  }
59
55
 
56
+ interface DropdownMenuTriggerRef extends PressableRef {
57
+ open: () => void;
58
+ close: () => void;
59
+ }
60
+
60
61
  export type {
61
62
  DropdownMenuCheckboxItemProps,
62
63
  DropdownMenuItemProps,
@@ -64,8 +65,8 @@ export type {
64
65
  DropdownMenuPortalProps,
65
66
  DropdownMenuRadioGroupProps,
66
67
  DropdownMenuRadioItemProps,
67
- DropdownMenuRootProps,
68
68
  DropdownMenuSeparatorProps,
69
69
  DropdownMenuSubProps,
70
70
  DropdownMenuSubTriggerProps,
71
+ DropdownMenuTriggerRef,
71
72
  };