'use client'; import { createContext, useContext, useRef } from 'react'; import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria'; import { type DisclosureGroupProps, type DisclosureGroupState, useDisclosureGroupState, useDisclosureState, } from 'react-stately'; import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect'; interface Props { groups: TDisclosureGroup[]; icon?: React.ReactNode; /** State key to use with a store */ selectStateKey?: string; /** Icon to display for the select */ selectIcon?: React.ReactNode; } type TDisclosureGroup = { key: string; label: string | React.ReactNode; tabs?: { key: string; label: string | React.ReactNode; body?: React.ReactNode; }[]; }; const DisclosureGroupStateContext = createContext(null); /** * Display an interactive OpenAPI disclosure group. */ export function OpenAPIDisclosureGroup(props: DisclosureGroupProps & Props) { const { icon, groups, selectStateKey, selectIcon } = props; const state = useDisclosureGroupState(props); return ( {groups.map((group) => ( ))} ); } function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode; selectStateKey?: string; selectIcon?: React.ReactNode; }) { const { icon, group, selectStateKey, selectIcon } = props; const defaultId = useId(); const id = group.key || defaultId; const groupState = useContext(DisclosureGroupStateContext); const isExpanded = groupState?.expandedKeys.has(id) || false; const state = useDisclosureState({ isExpanded, onExpandedChange() { if (groupState) { groupState.toggleKey(id); } }, }); const panelRef = useRef(null); const triggerRef = useRef(null); const isDisabled = groupState?.isDisabled || !group.tabs?.length || false; const { buttonProps: triggerProps, panelProps } = useDisclosure( { ...props, isExpanded, isDisabled, }, state, panelRef ); const { buttonProps } = useButton(triggerProps, triggerRef); const { isFocusVisible, focusProps } = useFocusRing(); const defaultTab = group.tabs?.[0]?.key || ''; const store = useSelectState(selectStateKey, defaultTab); const selectedTab = group.tabs?.find((tab) => tab.key === store.key) || group.tabs?.[0]; return (
{icon || ( )}
{group.label} {group.tabs ? (
e.stopPropagation()} > {group.tabs?.length > 1 ? ( { state.expand(); }} items={group.tabs} placement="bottom end" > {group.tabs.map((tab) => ( {tab.label} ))} ) : group.tabs[0]?.label ? ( {group.tabs[0].label} ) : null}
) : null}
{state.isExpanded && selectedTab && (
{selectedTab.body}
)}
); }