|
1 | 1 | 'use client'; |
2 | 2 |
|
3 | 3 | import { useMergeRefs } from '@floating-ui/react'; |
4 | | -import React, { useRef } from 'react'; |
| 4 | +import React, { useRef, version } from 'react'; |
5 | 5 |
|
6 | 6 | import { useTransition } from '../../hooks/use-transition'; |
7 | 7 | import { type ComponentProps, mergeProps, renderElement } from '../../utils/render-element'; |
8 | 8 | import { useTabsContext } from './tabs-context'; |
9 | 9 |
|
| 10 | +const major = parseInt(version, 10); |
| 11 | +const isModernReact = major >= 19 || major === 0; |
| 12 | + |
| 13 | +export function inertProps(active: boolean): Record<string, unknown> { |
| 14 | + if (!active) { |
| 15 | + return {}; |
| 16 | + } |
| 17 | + return { inert: isModernReact ? true : '' }; |
| 18 | +} |
| 19 | + |
10 | 20 | export interface TabsPanelProps extends ComponentProps<'div'> { |
11 | 21 | value: string; |
12 | 22 | /** When true, removes `hidden` so the panel stays in layout flow. */ |
@@ -51,11 +61,7 @@ export const TabsPanel = React.forwardRef<HTMLDivElement, TabsPanelProps>(functi |
51 | 61 | role: 'tabpanel' as const, |
52 | 62 | 'aria-labelledby': tabId, |
53 | 63 | tabIndex: 0, |
54 | | - // `inert` must be a truthy string, not a boolean or empty string, to stay |
55 | | - // correct across React 18 and 19: React 18 drops a boolean `true` and React |
56 | | - // 19 treats `''` as falsy. `'true'` renders the (presence-based) attribute in |
57 | | - // both. Matches the existing pattern in packages/ui PricingTableMatrix. |
58 | | - inert: !isSelected ? 'true' : undefined, |
| 64 | + ...inertProps(!isSelected), |
59 | 65 | hidden: !isSelected && !shouldForceMount ? true : undefined, |
60 | 66 | ref: combinedRef, |
61 | 67 | ...(shouldForceMount |
|
0 commit comments