@tamagui/context-menu 2.7.7 → 3.0.0-beta.637.1

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 (53) hide show
  1. package/dist/cjs/ContextMenu.cjs +152 -166
  2. package/dist/cjs/ContextMenu.native.cjs +178 -0
  3. package/dist/cjs/ContextMenu.native.js +157 -170
  4. package/dist/cjs/ContextMenu.native.js.map +1 -1
  5. package/dist/cjs/createNonNativeContextMenu.cjs +358 -422
  6. package/dist/cjs/createNonNativeContextMenu.native.cjs +405 -0
  7. package/dist/cjs/createNonNativeContextMenu.native.js +380 -443
  8. package/dist/cjs/createNonNativeContextMenu.native.js.map +1 -1
  9. package/dist/cjs/index.cjs +15 -29
  10. package/dist/cjs/index.native.cjs +29 -0
  11. package/dist/cjs/index.native.js +16 -29
  12. package/dist/cjs/index.native.js.map +1 -1
  13. package/dist/esm/ContextMenu.mjs +136 -140
  14. package/dist/esm/ContextMenu.mjs.map +1 -1
  15. package/dist/esm/ContextMenu.native.js +141 -145
  16. package/dist/esm/ContextMenu.native.js.map +1 -1
  17. package/dist/esm/createNonNativeContextMenu.mjs +342 -398
  18. package/dist/esm/createNonNativeContextMenu.mjs.map +1 -1
  19. package/dist/esm/createNonNativeContextMenu.native.js +365 -421
  20. package/dist/esm/createNonNativeContextMenu.native.js.map +1 -1
  21. package/dist/esm/index.js +3 -12
  22. package/dist/esm/index.js.map +1 -1
  23. package/dist/esm/index.mjs +3 -12
  24. package/dist/esm/index.mjs.map +1 -1
  25. package/dist/esm/index.native.js +3 -12
  26. package/dist/esm/index.native.js.map +1 -1
  27. package/dist/jsx/ContextMenu.mjs +136 -140
  28. package/dist/jsx/ContextMenu.mjs.map +1 -1
  29. package/dist/jsx/ContextMenu.native.cjs +178 -0
  30. package/dist/jsx/ContextMenu.native.js +157 -170
  31. package/dist/jsx/ContextMenu.native.js.map +1 -1
  32. package/dist/jsx/createNonNativeContextMenu.mjs +342 -398
  33. package/dist/jsx/createNonNativeContextMenu.mjs.map +1 -1
  34. package/dist/jsx/createNonNativeContextMenu.native.cjs +405 -0
  35. package/dist/jsx/createNonNativeContextMenu.native.js +380 -443
  36. package/dist/jsx/createNonNativeContextMenu.native.js.map +1 -1
  37. package/dist/jsx/index.js +3 -12
  38. package/dist/jsx/index.js.map +1 -1
  39. package/dist/jsx/index.mjs +3 -12
  40. package/dist/jsx/index.mjs.map +1 -1
  41. package/dist/jsx/index.native.cjs +29 -0
  42. package/dist/jsx/index.native.js +16 -29
  43. package/dist/jsx/index.native.js.map +1 -1
  44. package/package.json +9 -7
  45. package/src/ContextMenu.tsx +2 -3
  46. package/src/createNonNativeContextMenu.tsx +131 -104
  47. package/src/index.tsx +1 -13
  48. package/types/ContextMenu.d.ts +85 -76
  49. package/types/ContextMenu.d.ts.map +1 -1
  50. package/types/createNonNativeContextMenu.d.ts +77 -107
  51. package/types/createNonNativeContextMenu.d.ts.map +1 -1
  52. package/types/index.d.ts +84 -74
  53. package/types/index.d.ts.map +1 -1
@@ -1,7 +1,9 @@
1
- import type BaseMenuTypes from '@tamagui/create-menu'
2
- import { createBaseMenu, type CreateBaseMenuProps } from '@tamagui/create-menu'
1
+ import { createRefComponent } from '@tamagui/compose-refs'
2
+ import type * as BaseMenuTypes from '@tamagui/create-menu'
3
+ import { createBaseMenu } from '@tamagui/create-menu'
3
4
  import { useControllableState } from '@tamagui/use-controllable-state'
4
5
  import {
6
+ createStyledHOC,
5
7
  composeEventHandlers,
6
8
  composeRefs,
7
9
  createStyledContext,
@@ -44,6 +46,7 @@ type ContextMenuContextValue = {
44
46
 
45
47
  interface ContextMenuProps extends BaseMenuTypes.MenuProps {
46
48
  children?: React.ReactNode
49
+ defaultOpen?: boolean
47
50
  onOpenChange?(open: boolean, event?: ContextMenuOpenChangeEvent): void
48
51
  dir?: Direction
49
52
  modal?: boolean
@@ -55,7 +58,6 @@ interface ContextMenuTriggerProps extends ViewProps {
55
58
 
56
59
  type ContextMenuPortalProps = React.ComponentPropsWithoutRef<BaseMenu['Portal']>
57
60
 
58
- type ContextMenuContentElement = React.ComponentRef<BaseMenu['Content']>
59
61
  interface ContextMenuContentProps extends Omit<
60
62
  React.ComponentPropsWithoutRef<BaseMenu['Content']>,
61
63
  'onEntryFocus' | 'side' | 'sideOffset' | 'align'
@@ -85,13 +87,12 @@ interface ContextMenuSubProps extends BaseMenuTypes.MenuSubProps {
85
87
  }
86
88
 
87
89
  type ContextMenuSubTriggerProps = React.ComponentPropsWithoutRef<BaseMenu['SubTrigger']>
88
- type ContextMenuSubContentElement = React.ComponentRef<BaseMenu['Content']>
89
90
  type ContextMenuSubContentProps = React.ComponentPropsWithoutRef<BaseMenu['SubContent']>
90
91
 
91
92
  /* -----------------------------------------------------------------------------------------------*/
92
93
 
93
- export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
94
- const { Menu } = createBaseMenu(params)
94
+ export function createNonNativeContextMenu() {
95
+ const { Menu } = createBaseMenu()
95
96
 
96
97
  /* -------------------------------------------------------------------------------------------------
97
98
  * ContextMenu
@@ -103,8 +104,22 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
103
104
  createStyledContext<ContextMenuContextValue>()
104
105
 
105
106
  const ContextMenuComp = (props: ScopedProps<ContextMenuProps>) => {
106
- const { scope, children, onOpenChange, dir, modal = true, ...rest } = props
107
- const [open, setOpen] = React.useState(false)
107
+ const {
108
+ scope,
109
+ children,
110
+ onOpenChange,
111
+ open: openProp,
112
+ defaultOpen,
113
+ dir,
114
+ modal = true,
115
+ ...rest
116
+ } = props
117
+ // onOpenChange carries an event a caller can cancel, so it is called from
118
+ // handleOpenChange instead of through useControllableState's onChange
119
+ const [open, setOpen] = useControllableState({
120
+ prop: openProp,
121
+ defaultProp: defaultOpen ?? false,
122
+ })
108
123
  const triggerRef = React.useRef<TamaguiElement>(null)
109
124
 
110
125
  const handleOpenChange = React.useCallback(
@@ -113,7 +128,7 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
113
128
  if (event?.defaultPrevented) return
114
129
  setOpen(open)
115
130
  },
116
- [onOpenChange]
131
+ [onOpenChange, setOpen]
117
132
  )
118
133
 
119
134
  return (
@@ -148,9 +163,18 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
148
163
 
149
164
  const TRIGGER_NAME = 'ContextMenuTrigger'
150
165
 
151
- const ContextMenuTrigger = View.styleable<ScopedProps<ContextMenuTriggerProps>>(
152
- (props, forwardedRef) => {
153
- const { scope, style, disabled = false, asChild, children, ...triggerProps } = props
166
+ const ContextMenuTrigger = createStyledHOC(
167
+ View,
168
+ (props: ScopedProps<ContextMenuTriggerProps>, forwardedRef) => {
169
+ const {
170
+ scope,
171
+ style,
172
+ className,
173
+ disabled = false,
174
+ asChild,
175
+ children,
176
+ ...triggerProps
177
+ } = props
154
178
  const context = useContextMenuContext(scope)
155
179
  const pointRef = React.useRef<Point>({ x: 0, y: 0 })
156
180
  const virtualRef = React.useMemo(
@@ -223,7 +247,7 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
223
247
  <Menu.Anchor scope={scope || CONTEXTMENU_CONTEXT} virtualRef={virtualRef} />
224
248
  <Comp
225
249
  render="span"
226
- componentName={TRIGGER_NAME}
250
+ className={`is_${TRIGGER_NAME} ${className || ''}`.trim()}
227
251
  id={context.triggerId}
228
252
  data-state={context.open ? 'open' : 'closed'}
229
253
  data-disabled={disabled ? '' : undefined}
@@ -319,36 +343,37 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
319
343
 
320
344
  const CONTENT_NAME = 'ContextMenuContent'
321
345
 
322
- const ContextMenuContent = React.forwardRef<
323
- ContextMenuContentElement,
324
- ScopedProps<ContextMenuContentProps>
325
- >((props, forwardedRef) => {
326
- const { scope, ...contentProps } = props
327
- const context = useContextMenuContext(scope)
328
- const hasInteractedOutsideRef = React.useRef(false)
346
+ const ContextMenuContent = createStyledHOC(
347
+ Menu.Content,
348
+ (props: ScopedProps<ContextMenuContentProps>, forwardedRef) => {
349
+ const { scope, ...contentProps } = props
350
+ const context = useContextMenuContext(scope)
351
+ const hasInteractedOutsideRef = React.useRef(false)
329
352
 
330
- return (
331
- <Menu.Content
332
- id={context.contentId}
333
- aria-labelledby={context.triggerId}
334
- scope={scope || CONTEXTMENU_CONTEXT}
335
- {...contentProps}
336
- ref={forwardedRef}
337
- onCloseAutoFocus={composeEventHandlers(props.onCloseAutoFocus, (event) => {
338
- if (!hasInteractedOutsideRef.current) context.triggerRef.current?.focus()
339
- hasInteractedOutsideRef.current = false
340
- event.preventDefault()
341
- })}
342
- onInteractOutside={composeEventHandlers(props.onInteractOutside, (event) => {
343
- const originalEvent = event.detail.originalEvent as PointerEvent
344
- const ctrlLeftClick =
345
- originalEvent.button === 0 && originalEvent.ctrlKey === true
346
- const isRightClick = originalEvent.button === 2 || ctrlLeftClick
347
- if (!context.modal || isRightClick) hasInteractedOutsideRef.current = true
348
- })}
349
- />
350
- )
351
- })
353
+ return (
354
+ <Menu.Content
355
+ id={context.contentId}
356
+ aria-labelledby={context.triggerId}
357
+ scope={scope || CONTEXTMENU_CONTEXT}
358
+ {...contentProps}
359
+ ref={forwardedRef}
360
+ onCloseAutoFocus={composeEventHandlers(props.onCloseAutoFocus, (event) => {
361
+ if (!hasInteractedOutsideRef.current) context.triggerRef.current?.focus()
362
+ hasInteractedOutsideRef.current = false
363
+ event.cancel()
364
+ })}
365
+ onInteractOutside={composeEventHandlers(props.onInteractOutside, (event) => {
366
+ if (event.interaction !== 'pointer' || !event.event) return
367
+ const originalEvent = event.event
368
+ const ctrlLeftClick =
369
+ originalEvent.button === 0 && originalEvent.ctrlKey === true
370
+ const isRightClick = originalEvent.button === 2 || ctrlLeftClick
371
+ if (!context.modal || isRightClick) hasInteractedOutsideRef.current = true
372
+ })}
373
+ />
374
+ )
375
+ }
376
+ )
352
377
 
353
378
  ContextMenuContent.displayName = CONTENT_NAME
354
379
 
@@ -358,14 +383,14 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
358
383
 
359
384
  const ITEM_NAME = 'ContextMenuItem'
360
385
 
361
- const ContextMenuItem = React.forwardRef<
386
+ const ContextMenuItem = createRefComponent<
362
387
  TamaguiElement,
363
388
  ScopedProps<ContextMenuItemProps>
364
389
  >((props, forwardedRef) => {
365
- const { scope, ...itemProps } = props
390
+ const { scope, className, ...itemProps } = props
366
391
  return (
367
392
  <Menu.Item
368
- componentName={ITEM_NAME}
393
+ className={`is_${ITEM_NAME} ${className || ''}`.trim()}
369
394
  scope={scope || CONTEXTMENU_CONTEXT}
370
395
  {...itemProps}
371
396
  ref={forwardedRef}
@@ -381,14 +406,14 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
381
406
 
382
407
  const CHECKBOX_ITEM_NAME = 'ContextMenuCheckboxItem'
383
408
 
384
- const ContextMenuCheckboxItem = React.forwardRef<
409
+ const ContextMenuCheckboxItem = createRefComponent<
385
410
  TamaguiElement,
386
411
  ScopedProps<ContextMenuCheckboxItemProps>
387
412
  >((props, forwardedRef) => {
388
- const { scope, ...checkboxItemProps } = props
413
+ const { scope, className, ...checkboxItemProps } = props
389
414
  return (
390
415
  <Menu.CheckboxItem
391
- componentName={CHECKBOX_ITEM_NAME}
416
+ className={`is_${CHECKBOX_ITEM_NAME} ${className || ''}`.trim()}
392
417
  scope={scope || CONTEXTMENU_CONTEXT}
393
418
  {...checkboxItemProps}
394
419
  ref={forwardedRef}
@@ -404,7 +429,7 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
404
429
 
405
430
  const RADIO_GROUP_NAME = 'ContextMenuRadioGroup'
406
431
 
407
- const ContextMenuRadioGroup = React.forwardRef<
432
+ const ContextMenuRadioGroup = createRefComponent<
408
433
  ContextMenuRadioGroupElement,
409
434
  ScopedProps<ContextMenuRadioGroupProps>
410
435
  >((props, forwardedRef) => {
@@ -426,14 +451,14 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
426
451
 
427
452
  const RADIO_ITEM_NAME = 'ContextMenuRadioItem'
428
453
 
429
- const ContextMenuRadioItem = React.forwardRef<
454
+ const ContextMenuRadioItem = createRefComponent<
430
455
  TamaguiElement,
431
456
  ScopedProps<ContextMenuRadioItemProps>
432
457
  >((props, forwardedRef) => {
433
- const { scope, ...radioItemProps } = props
458
+ const { scope, className, ...radioItemProps } = props
434
459
  return (
435
460
  <Menu.RadioItem
436
- componentName={RADIO_ITEM_NAME}
461
+ className={`is_${RADIO_ITEM_NAME} ${className || ''}`.trim()}
437
462
  scope={scope || CONTEXTMENU_CONTEXT}
438
463
  {...radioItemProps}
439
464
  ref={forwardedRef}
@@ -449,19 +474,20 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
449
474
 
450
475
  const INDICATOR_NAME = 'ContextMenuItemIndicator'
451
476
 
452
- const ContextMenuItemIndicator = Menu.ItemIndicator.styleable<
453
- ScopedProps<ContextMenuItemIndicatorProps>
454
- >((props, forwardedRef) => {
455
- const { scope, ...itemIndicatorProps } = props
456
- return (
457
- <Menu.ItemIndicator
458
- componentName={INDICATOR_NAME}
459
- scope={scope || CONTEXTMENU_CONTEXT}
460
- {...itemIndicatorProps}
461
- ref={forwardedRef}
462
- />
463
- )
464
- })
477
+ const ContextMenuItemIndicator = createStyledHOC(
478
+ Menu.ItemIndicator,
479
+ (props: ScopedProps<ContextMenuItemIndicatorProps>, forwardedRef) => {
480
+ const { scope, className, ...itemIndicatorProps } = props
481
+ return (
482
+ <Menu.ItemIndicator
483
+ className={`is_${INDICATOR_NAME} ${className || ''}`.trim()}
484
+ scope={scope || CONTEXTMENU_CONTEXT}
485
+ {...itemIndicatorProps}
486
+ ref={forwardedRef}
487
+ />
488
+ )
489
+ }
490
+ )
465
491
 
466
492
  ContextMenuItemIndicator.displayName = INDICATOR_NAME
467
493
 
@@ -499,12 +525,13 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
499
525
 
500
526
  const SUB_TRIGGER_NAME = 'ContextMenuSubTrigger'
501
527
 
502
- const ContextMenuSubTrigger = View.styleable<ScopedProps<ContextMenuSubTriggerProps>>(
503
- (props, forwardedRef) => {
504
- const { scope, ...subTriggerProps } = props
528
+ const ContextMenuSubTrigger = createStyledHOC(
529
+ View,
530
+ (props: ScopedProps<ContextMenuSubTriggerProps>, forwardedRef) => {
531
+ const { scope, className, ...subTriggerProps } = props
505
532
  return (
506
533
  <Menu.SubTrigger
507
- componentName={SUB_TRIGGER_NAME}
534
+ className={`is_${SUB_TRIGGER_NAME} ${className || ''}`.trim()}
508
535
  scope={scope || CONTEXTMENU_CONTEXT}
509
536
  {...subTriggerProps}
510
537
  ref={forwardedRef}
@@ -521,38 +548,38 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
521
548
 
522
549
  const SUB_CONTENT_NAME = 'ContextMenuSubContent'
523
550
 
524
- const ContextMenuSubContent = React.forwardRef<
525
- ContextMenuSubContentElement,
526
- ScopedProps<ContextMenuSubContentProps>
527
- >((props, forwardedRef) => {
528
- const { scope, ...subContentProps } = props
529
- return (
530
- <Menu.SubContent
531
- scope={scope || CONTEXTMENU_CONTEXT}
532
- {...subContentProps}
533
- ref={forwardedRef}
534
- style={
535
- isWeb
536
- ? {
537
- ...(props.style as object),
538
- ...({
539
- '--tamagui-context-menu-content-transform-origin':
540
- 'var(--tamagui-popper-transform-origin)',
541
- '--tamagui-context-menu-content-available-width':
542
- 'var(--tamagui-popper-available-width)',
543
- '--tamagui-context-menu-content-available-height':
544
- 'var(--tamagui-popper-available-height)',
545
- '--tamagui-context-menu-trigger-width':
546
- 'var(--tamagui-popper-anchor-width)',
547
- '--tamagui-context-menu-trigger-height':
548
- 'var(--tamagui-popper-anchor-height)',
549
- } as React.CSSProperties),
550
- }
551
- : null
552
- }
553
- />
554
- )
555
- })
551
+ const ContextMenuSubContent = createStyledHOC(
552
+ Menu.SubContent,
553
+ (props: ScopedProps<ContextMenuSubContentProps>, forwardedRef) => {
554
+ const { scope, ...subContentProps } = props
555
+ return (
556
+ <Menu.SubContent
557
+ scope={scope || CONTEXTMENU_CONTEXT}
558
+ {...subContentProps}
559
+ ref={forwardedRef}
560
+ style={
561
+ isWeb
562
+ ? {
563
+ ...(props.style as object),
564
+ ...({
565
+ '--tamagui-context-menu-content-transform-origin':
566
+ 'var(--tamagui-popper-transform-origin)',
567
+ '--tamagui-context-menu-content-available-width':
568
+ 'var(--tamagui-popper-available-width)',
569
+ '--tamagui-context-menu-content-available-height':
570
+ 'var(--tamagui-popper-available-height)',
571
+ '--tamagui-context-menu-trigger-width':
572
+ 'var(--tamagui-popper-anchor-width)',
573
+ '--tamagui-context-menu-trigger-height':
574
+ 'var(--tamagui-popper-anchor-height)',
575
+ } as React.CSSProperties),
576
+ }
577
+ : null
578
+ }
579
+ />
580
+ )
581
+ }
582
+ )
556
583
 
557
584
  ContextMenuSubContent.displayName = SUB_CONTENT_NAME
558
585
 
@@ -562,14 +589,14 @@ export function createNonNativeContextMenu(params: CreateBaseMenuProps) {
562
589
 
563
590
  const ARROW_NAME = 'ContextMenuArrow'
564
591
 
565
- const ContextMenuArrow = React.forwardRef<
592
+ const ContextMenuArrow = createRefComponent<
566
593
  TamaguiElement,
567
594
  ScopedProps<ContextMenuArrowProps>
568
595
  >((props, forwardedRef) => {
569
- const { scope, ...arrowProps } = props
596
+ const { scope, className, ...arrowProps } = props
570
597
  return (
571
598
  <Menu.Arrow
572
- componentName={ARROW_NAME}
599
+ className={`is_${ARROW_NAME} ${className || ''}`.trim()}
573
600
  scope={scope || CONTEXTMENU_CONTEXT}
574
601
  {...arrowProps}
575
602
  ref={forwardedRef}
package/src/index.tsx CHANGED
@@ -1,17 +1,5 @@
1
1
  import '@tamagui/polyfill-dev'
2
2
 
3
- import { MenuPredefined } from '@tamagui/create-menu'
4
-
5
3
  import { createContextMenu } from './ContextMenu'
6
4
 
7
- export const ContextMenu = createContextMenu({
8
- Icon: MenuPredefined.MenuIcon,
9
- Image: MenuPredefined.MenuImage,
10
- Indicator: MenuPredefined.MenuIndicator,
11
- Item: MenuPredefined.MenuItem,
12
- Label: MenuPredefined.MenuLabel,
13
- MenuGroup: MenuPredefined.MenuGroup,
14
- Separator: MenuPredefined.MenuSeparator,
15
- SubTitle: MenuPredefined.SubTitle,
16
- Title: MenuPredefined.Title,
17
- })
5
+ export const ContextMenu = createContextMenu()