import React, { FormHTMLAttributes, HTMLProps, InputHTMLAttributes, Ref } from 'react';
export type TreeItemIndex = string | number;
export interface TreeItem {
index: TreeItemIndex;
children?: Array;
isFolder?: boolean;
canMove?: boolean;
canRename?: boolean;
data: T;
}
export interface TreePosition {
treeId: string;
parent: TreeItemIndex;
index: number;
}
export interface TreeItemActions {
primaryAction: () => void;
startRenamingItem: () => void;
stopRenamingItem: () => void;
expandItem: () => void;
collapseItem: () => void;
toggleExpandedState: () => void;
selectItem: () => void;
unselectItem: () => void;
addToSelectedItems: () => void;
selectUpTo: (overrideOldSelection?: boolean) => void;
startDragging: () => void;
/** @param setDomFocus - Defaults to true. */
focusItem: (setDomFocus?: boolean) => void;
}
export interface TreeItemRenderFlags {
isSelected?: boolean;
isExpanded?: boolean;
isFocused?: boolean;
isRenaming?: boolean;
isDraggingOver?: boolean;
isDraggingOverParent?: boolean;
isSearchMatching?: boolean;
canDrag?: boolean;
canDropOn?: boolean;
}
export interface TreeItemRenderContext extends TreeItemActions, TreeItemRenderFlags {
interactiveElementProps: HTMLProps;
itemContainerWithoutChildrenProps: HTMLProps;
itemContainerWithChildrenProps: HTMLProps;
arrowProps: HTMLProps;
viewStateFlags: {
[collection in C]: boolean;
};
}
export interface TreeInformation extends TreeConfiguration {
areItemsSelected: boolean;
isRenaming: boolean;
isFocused: boolean;
isSearching: boolean;
search: string | null;
isProgrammaticallyDragging: boolean;
}
export interface TreeRenderProps {
renderItem?: (props: {
item: TreeItem;
depth: number;
children: React.ReactNode | null;
title: React.ReactNode;
arrow: React.ReactNode;
context: TreeItemRenderContext;
info: TreeInformation;
}) => React.ReactElement | null;
renderItemTitle?: (props: {
title: string;
item: TreeItem;
context: TreeItemRenderContext;
info: TreeInformation;
}) => React.ReactElement | null | string;
renderItemArrow?: (props: {
item: TreeItem;
context: TreeItemRenderContext;
info: TreeInformation;
}) => React.ReactElement | null;
renderRenameInput?: (props: {
item: TreeItem;
inputProps: InputHTMLAttributes;
inputRef: Ref;
submitButtonProps: HTMLProps;
submitButtonRef: Ref;
formProps: FormHTMLAttributes;
}) => React.ReactElement | null;
renderItemsContainer?: (props: {
children: React.ReactNode;
containerProps: HTMLProps;
info: TreeInformation;
depth: number;
parentId: TreeItemIndex;
}) => React.ReactElement | null;
renderTreeContainer?: (props: {
children: React.ReactNode;
containerProps: HTMLProps;
info: TreeInformation;
}) => React.ReactElement | null;
renderDragBetweenLine?: (props: {
draggingPosition: DraggingPosition;
lineProps: HTMLProps;
}) => React.ReactElement | null;
renderSearchInput?: (props: {
inputProps: HTMLProps;
}) => React.ReactElement | null;
renderLiveDescriptorContainer?: (props: {
children: React.ReactNode;
tree: TreeConfiguration;
}) => React.ReactElement | null;
renderDepthOffset?: number;
}
export type AllTreeRenderProps = Required>;
export declare enum InteractionMode {
DoubleClickItemToExpand = "double-click-item-to-expand",
ClickItemToExpand = "click-item-to-expand",
ClickArrowToExpand = "click-arrow-to-expand"
}
export interface InteractionManager {
mode: InteractionMode | string;
extends?: InteractionMode;
createInteractiveElementProps: (item: TreeItem, treeId: string, actions: TreeItemActions, renderFlags: TreeItemRenderFlags,
/** See https://github.com/lukasbach/react-complex-tree/issues/48 */
__unsafeViewState?: IndividualTreeViewState) => HTMLProps;
}
export interface TreeCapabilities {
defaultInteractionMode?: InteractionMode | InteractionManager;
canDragAndDrop?: boolean;
canDropOnFolder?: boolean;
canDropOnNonFolder?: boolean;
canReorderItems?: boolean;
canDrag?: (items: TreeItem[]) => boolean;
canDropAt?: (items: TreeItem[], target: DraggingPosition) => boolean;
canInvokePrimaryActionOnItemContainer?: boolean;
canSearch?: boolean;
canSearchByStartingTyping?: boolean;
canRename?: boolean;
autoFocus?: boolean;
doesSearchMatchItem?: (search: string, item: TreeItem, itemTitle: string) => boolean;
showLiveDescription?: boolean;
shouldRenderChildren?: (item: TreeItem, context: TreeItemRenderContext) => boolean;
/**
* See Issue #148 or the sample at
* https://rct.lukasbach.com/storybook/?path=/story/core-basic-examples--single-tree?path=/story/core-drag-and-drop-configurability--can-drop-below-open-folders
* for details.
*
* If enabled, dropping at the bottom of an open folder will drop the items
* in the parent folder below the hovered item instead of inside the folder
* at the top.
*/
canDropBelowOpenFolders?: boolean;
disableArrowKeys?: boolean;
}
export type IndividualTreeViewState = {
selectedItems?: TreeItemIndex[];
expandedItems?: TreeItemIndex[];
focusedItem?: TreeItemIndex;
} & {
[c in C]: TreeItemIndex | TreeItemIndex[] | undefined;
};
export interface TreeViewState {
[treeId: string]: IndividualTreeViewState | undefined;
}
export interface ExplicitDataSource {
items: Record>;
}
export interface ImplicitDataSource {
dataProvider: TreeDataProvider;
}
export type DataSource = ExplicitDataSource | ImplicitDataSource;
export interface TreeChangeHandlers {
onStartRenamingItem?: (item: TreeItem, treeId: string) => void;
onRenameItem?: (item: TreeItem, name: string, treeId: string) => void;
onAbortRenamingItem?: (item: TreeItem, treeId: string) => void;
onCollapseItem?: (item: TreeItem, treeId: string) => void;
onExpandItem?: (item: TreeItem, treeId: string) => void;
onSelectItems?: (items: TreeItemIndex[], treeId: string) => void;
onFocusItem?: (item: TreeItem, treeId: string, setDomFocus?: boolean) => void;
onDrop?: (items: TreeItem[], target: DraggingPosition) => void;
onPrimaryAction?: (items: TreeItem, treeId: string) => void;
onRegisterTree?: (tree: TreeConfiguration) => void;
onUnregisterTree?: (tree: TreeConfiguration) => void;
onMissingItems?: (itemIds: TreeItemIndex[]) => void;
onMissingChildren?: (itemIds: TreeItemIndex[]) => void;
}
export interface TreeEnvironmentChangeActions {
focusTree: (treeId: string, autoFocus?: boolean) => void;
renameItem: (itemId: TreeItemIndex, name: string, treeId: string) => void;
collapseItem: (itemId: TreeItemIndex, treeId: string) => void;
expandItem: (itemId: TreeItemIndex, treeId: string) => void;
toggleItemExpandedState: (itemId: TreeItemIndex, treeId: string) => void;
selectItems: (itemsIds: TreeItemIndex[], treeId: string) => void;
toggleItemSelectStatus: (itemId: TreeItemIndex, treeId: string) => void;
invokePrimaryAction: (itemId: TreeItemIndex, treeID: string) => void;
/** @param setDomFocus - Defaults to true. */
focusItem: (itemId: TreeItemIndex, treeId: string, setDomFocus?: boolean) => void;
moveFocusUp: (treeId: string) => void;
moveFocusDown: (treeId: string) => void;
startProgrammaticDrag: () => void;
abortProgrammaticDrag: () => void;
completeProgrammaticDrag: () => void;
moveProgrammaticDragPositionUp: () => void;
moveProgrammaticDragPositionDown: () => void;
expandAll: (treeId: string) => void;
collapseAll: (treeId: string) => void;
expandSubsequently: (treeId: string, itemIds: TreeItemIndex[]) => Promise;
}
export type TreeEnvironmentActionsContextProps = TreeEnvironmentChangeActions;
export interface TreeEnvironmentRef extends TreeEnvironmentChangeActions, Omit, keyof TreeChangeHandlers> {
treeEnvironmentContext: TreeEnvironmentContextProps;
dragAndDropContext: DragAndDropContextProps;
activeTreeId?: string;
treeIds: string[];
trees: Record;
}
export interface TreeEnvironmentConfiguration extends TreeRenderProps, TreeCapabilities, TreeChangeHandlers, ExplicitDataSource {
viewState: TreeViewState;
keyboardBindings?: KeyboardBindings;
liveDescriptors?: LiveDescriptors;
getItemTitle: (item: TreeItem) => string;
}
export interface TreeEnvironmentContextProps extends Omit, keyof TreeRenderProps>, AllTreeRenderProps {
registerTree: (tree: TreeConfiguration) => void;
unregisterTree: (treeId: string) => void;
activeTreeId?: string;
setActiveTree: (treeIdOrSetStateFunction: string | undefined | ((prevState: string | undefined) => string | undefined), autoFocus?: boolean) => void;
treeIds: string[];
trees: Record;
linearItems: Record;
}
export interface DragAndDropContextProps {
onStartDraggingItems: (items: TreeItem[], treeId: string) => void;
draggingItems?: TreeItem[];
itemHeight: number;
isProgrammaticallyDragging?: boolean;
startProgrammaticDrag: () => void;
abortProgrammaticDrag: () => void;
completeProgrammaticDrag: () => void;
programmaticDragUp: () => void;
programmaticDragDown: () => void;
draggingPosition?: DraggingPosition;
viableDragPositions?: {
[treeId: string]: DraggingPosition[];
};
linearItems?: Array<{
item: TreeItemIndex;
depth: number;
}>;
onDragOverTreeHandler: (e: DragEvent, treeId: string, containerRef: React.MutableRefObject) => void;
onDragLeaveContainerHandler: (e: DragEvent, containerRef: React.MutableRefObject) => void;
}
export type DraggingPosition = DraggingPositionItem | DraggingPositionBetweenItems | DraggingPositionRoot;
export interface AbstractDraggingPosition {
targetType: 'item' | 'between-items' | 'root';
treeId: string;
linearIndex: number;
depth: number;
}
export interface DraggingPositionItem extends AbstractDraggingPosition {
targetType: 'item';
targetItem: TreeItemIndex;
parentItem: TreeItemIndex;
}
export interface DraggingPositionBetweenItems extends AbstractDraggingPosition {
targetType: 'between-items';
childIndex: number;
linePosition: 'top' | 'bottom';
parentItem: TreeItemIndex;
}
export interface DraggingPositionRoot extends AbstractDraggingPosition {
targetType: 'root';
targetItem: TreeItemIndex;
}
export interface ControlledTreeEnvironmentProps extends TreeEnvironmentConfiguration {
children?: React.ReactElement | (React.ReactElement | null)[] | null;
}
export interface UncontrolledTreeEnvironmentProps extends TreeRenderProps, TreeCapabilities, ImplicitDataSource, TreeChangeHandlers {
viewState: TreeViewState;
keyboardBindings?: KeyboardBindings;
liveDescriptors?: LiveDescriptors;
getItemTitle: (item: TreeItem) => string;
children: React.ReactElement | (React.ReactElement | null)[] | null;
disableMultiselect?: boolean;
}
export interface TreeConfiguration {
treeId: string;
rootItem: string;
treeLabel?: string;
treeLabelledBy?: string;
}
export interface TreeProps extends TreeConfiguration, Partial> {
}
export interface TreeContextProps extends TreeConfiguration {
search: string | null;
setSearch: (searchValue: string | null) => void;
renamingItem: TreeItemIndex | null;
setRenamingItem: (item: TreeItemIndex | null) => void;
renderers: AllTreeRenderProps;
treeInformation: TreeInformation;
getItemsLinearly: () => Array<{
item: TreeItemIndex;
depth: number;
}>;
}
export interface TreeChangeActions {
focusTree: (autoFocus?: boolean) => void;
startRenamingItem: (itemId: TreeItemIndex) => void;
stopRenamingItem: () => void;
completeRenamingItem: () => void;
abortRenamingItem: () => void;
renameItem: (itemId: TreeItemIndex, name: string) => void;
collapseItem: (itemId: TreeItemIndex) => void;
expandItem: (itemId: TreeItemIndex) => void;
toggleItemExpandedState: (itemId: TreeItemIndex) => void;
selectItems: (itemsIds: TreeItemIndex[]) => void;
toggleItemSelectStatus: (itemId: TreeItemIndex) => void;
/** @param setDomFocus - Defaults to true. */
focusItem: (itemId: TreeItemIndex, setDomFocus?: boolean) => void;
moveFocusUp: () => void;
moveFocusDown: () => void;
invokePrimaryAction: (itemId: TreeItemIndex) => void;
setSearch: (search: string | null) => void;
abortSearch: () => void;
expandAll: () => void;
collapseAll: () => void;
expandSubsequently: (itemIds: TreeItemIndex[]) => Promise;
}
export type TreeChangeActionsContextProps = TreeChangeActions;
export interface TreeRef extends TreeChangeActions, TreeInformation {
treeContext: TreeContextProps;
treeEnvironmentContext: TreeEnvironmentContextProps;
dragAndDropContext: DragAndDropContextProps;
search: string | null;
}
export interface TreeDataProvider {
onDidChangeTreeData?: (listener: (changedItemIds: TreeItemIndex[]) => void) => Disposable;
getTreeItem: (itemId: TreeItemIndex) => Promise>;
getTreeItems?: (itemIds: TreeItemIndex[]) => Promise;
onRenameItem?: (item: TreeItem, name: string) => Promise;
onChangeItemChildren?: (itemId: TreeItemIndex, newChildren: TreeItemIndex[]) => Promise;
}
export type Disposable = {
dispose: () => void;
};
export interface LinearItem {
item: TreeItemIndex;
depth: number;
}
export interface KeyboardBindings {
primaryAction?: string[];
moveFocusToFirstItem?: string[];
moveFocusToLastItem?: string[];
expandSiblings?: string[];
renameItem?: string[];
abortRenameItem?: string[];
toggleSelectItem?: string[];
abortSearch?: string[];
startSearch?: string[];
selectAll?: string[];
startProgrammaticDnd?: string[];
abortProgrammaticDnd?: string[];
completeProgrammaticDnd?: string[];
}
/**
* Live descriptors are written in an aria live region describing the state of the
* tree to accessibility readers. They are displayed in a visually hidden area at the
* bottom of the tree. Each descriptor composes a HTML string. Variables in the form
* of \{variableName\} can be used.
*
* The \{keybinding:bindingname\} variable refers to a specific keybinding, i.e. \{keybinding:primaryAction\}
* is a valid variable.
*
* See the implementation of the `defaultLiveDescriptors` for more details.
*/
export interface LiveDescriptors {
/**
* Supports the following variables:
* \{treeLabel\} \{keybinding:bindingname\}
*/
introduction: string;
/**
* Supports the following variables:
* \{renamingItem\} \{keybinding:bindingname\}
*/
renamingItem: string;
/**
* Supports the following variables:
* \{keybinding:bindingname\}
*/
searching: string;
/**
* Supports the following variables:
* \{dropTarget\} \{dragItems\} \{keybinding:bindingname\}
*/
programmaticallyDragging: string;
/**
* Will be displayed in addition to the programmaticallyDragging description,
* but with the aria-live attribute assertive.
*
* Supports the following variables:
* \{dropTarget\} \{dragItems\} \{keybinding:bindingname\}
*/
programmaticallyDraggingTarget: string;
}
export type HoveringPosition = {
linearIndex: number;
offset: 'bottom' | 'top' | undefined;
indentation: number | undefined;
};