// // Copyright 2024 DXOS.org // import { useArrowNavigationGroup, useFocusableGroup } from '@fluentui/react-tabster'; import { createContextScope, type Scope } from '@radix-ui/react-context'; import { Primitive } from '@radix-ui/react-primitive'; import { Slot } from '@radix-ui/react-slot'; import { useControllableState } from '@radix-ui/react-use-controllable-state'; import React, { type ComponentPropsWithRef, type CSSProperties, forwardRef } from 'react'; import { useThemeContext } from '../../hooks'; import { type ThemedClassName } from '../../util'; // TODO(thure): A lot of the accessible affordances for this kind of thing need to be implemented per https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/treegrid_role const TREEGRID_ROW_NAME = 'TreegridRow'; type TreegridRowScopedProps

= P & { __treegridRowScope?: Scope }; const [createTreegridRowContext, createTreegridRowScope] = createContextScope(TREEGRID_ROW_NAME, []); type TreegridRowContextValue = { open?: boolean; onOpenChange?: (nextOpen: boolean) => void; }; const [TreegridRowProvider, useTreegridRowContext] = createTreegridRowContext(TREEGRID_ROW_NAME); const PATH_SEPARATOR = '~'; const PARENT_OF_SEPARATOR = ' '; type TreegridRootProps = ThemedClassName> & { gridTemplateColumns: CSSProperties['gridTemplateColumns']; asChild?: boolean; }; const TreegridRoot = forwardRef( ({ asChild, classNames, children, style, gridTemplateColumns, ...props }, forwardedRef) => { const { tx } = useThemeContext(); const Root = asChild ? Slot : Primitive.div; const arrowNavigationAttrs = useArrowNavigationGroup({ axis: 'vertical', tabbable: false, circular: true }); return ( {children} ); }, ); type TreegridRowProps = ThemedClassName> & { id: string; asChild?: boolean; parentOf?: string; defaultOpen?: boolean; open?: boolean; onOpenChange?(open: boolean): void; }; const TreegridRow = forwardRef>( ( { __treegridRowScope, asChild, classNames, children, id, parentOf, open: propsOpen, defaultOpen, onOpenChange: propsOnOpenChange, ...props }, forwardedRef, ) => { const { tx } = useThemeContext(); const Root = asChild ? Slot : Primitive.div; const pathParts = id.split(PATH_SEPARATOR); const level = pathParts.length - 1; const [open, onOpenChange] = useControllableState({ prop: propsOpen, onChange: propsOnOpenChange, defaultProp: defaultOpen, }); const focusableGroupAttrs = useFocusableGroup({ tabBehavior: 'limited' }); const arrowGroupAttrs = useArrowNavigationGroup({ axis: 'horizontal', tabbable: false, circular: false, memorizeCurrent: false, }); return (

{children}
); }, ); type TreegridCellProps = ThemedClassName> & { indent?: boolean }; const TreegridCell = forwardRef( ({ classNames, children, indent, ...props }, forwardedRef) => { const { tx } = useThemeContext(); return (
{children}
); }, ); export type { TreegridRootProps, TreegridRowProps }; export const Treegrid = { Root: TreegridRoot, Row: TreegridRow, Cell: TreegridCell, PARENT_OF_SEPARATOR, PATH_SEPARATOR, createTreegridRowScope, useTreegridRowContext, };