/* eslint-disable react/prop-types */ import React, { useCallback } from 'react'; import type { CSSProperties } from 'react'; import { useFocusRing, css, cx, mergeProps, spacing, } from '@mongodb-js/compass-components'; import type { Actions } from './constants'; export type VirtualListItemProps = { style?: CSSProperties; }; export type TreeItemProps = { id: string; posInSet: number; setSize: number; isTabbable: boolean; }; export type NamespaceItemProps = { name: string; type: string; isActive: boolean; isReadOnly: boolean; onNamespaceAction(namespace: string, action: Actions): void; }; export function useDefaultAction( onDefaultAction: (evt: React.KeyboardEvent | React.MouseEvent) => void ): React.HTMLAttributes { const onClick = useCallback( (evt: React.MouseEvent) => { evt.stopPropagation(); onDefaultAction(evt); }, [onDefaultAction] ); const onKeyDown = useCallback( (evt: React.KeyboardEvent) => { if ( // Only handle keyboard events if they originated on the element evt.target === evt.currentTarget && [' ', 'Enter'].includes(evt.key) ) { evt.preventDefault(); evt.stopPropagation(); onDefaultAction(evt); } }, [onDefaultAction] ); return { onClick, onKeyDown }; } const itemContainer = css({ cursor: 'pointer', color: 'var(--item-color)', '& .item-action-controls': { marginLeft: 'auto', marginRight: spacing[1], }, '&:hover .item-background': { display: 'block', backgroundColor: 'var(--item-bg-color-hover)', }, '& .item-action-controls:hover + .item-background': { display: 'none', }, svg: { flexShrink: 0, }, }); const activeItemContainer = css({ color: 'var(--item-color-active)', fontWeight: 'bold', '.item-background, :hover .item-background': { backgroundColor: 'var(--item-bg-color-active)', }, // this is copied from leafygreen's own navigation, hence the pixel values '::before': { zIndex: 1, backgroundColor: 'var(--item-color-active)', content: '""', position: 'absolute', left: '0px', top: '6px', bottom: '6px', width: '4px', borderRadius: '0px 6px 6px 0px', }, }); const itemWrapper = css({ position: 'relative', display: 'flex', alignItems: 'center', }); const itemBackground = css({ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', zIndex: -1, }); const itemButtonWrapper = css({ display: 'flex', alignItems: 'center', minWidth: 0, }); const itemLabel = css({ overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', }); export const ItemContainer: React.FunctionComponent< { id: string; level: number; setSize: number; posInSet: number; isExpanded?: boolean; isActive?: boolean; isTabbable?: boolean; onDefaultAction( evt: | React.KeyboardEvent | React.MouseEvent ): void; } & React.HTMLProps > = ({ id, level, setSize, posInSet, isExpanded, isActive, isTabbable, onDefaultAction, children, className, ...props }) => { const focusRingProps = useFocusRing(); const defaultActionProps = useDefaultAction(onDefaultAction); const extraCSS = []; if (isActive) { extraCSS.push(activeItemContainer); } const treeItemProps = mergeProps( { role: 'treeitem', 'aria-level': level, 'aria-setsize': setSize, 'aria-posinset': posInSet, 'aria-expanded': isExpanded, tabIndex: isTabbable ? 0 : -1, className: cx(itemContainer, ...extraCSS, className), }, props, defaultActionProps, focusRingProps ); return (
{children}
); }; export const ItemWrapper: React.FunctionComponent< React.HTMLProps > = ({ className, children }) => { return (
{children}
); }; export const ItemButtonWrapper: React.FunctionComponent< React.HTMLProps > = ({ className, children }) => { return
{children}
; }; export const ItemLabel: React.FunctionComponent< React.HTMLProps > = ({ children, className, ...props }) => { return ( {children} ); };