//
// 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,
};