import * as React from "react"; import { IReadonlyObservableValue } from '../../Core/Observable'; import { IFocusZoneProps } from '../../FocusZone'; import { IBehavior } from '../../Utilities/Behavior'; import { IEventDispatch } from '../../Utilities/Dispatch'; import { IItemProvider } from '../../Utilities/Provider'; import { IIndexed, ISelectableUI, ISelection, ISelectOptions } from '../../Utilities/Selection'; import { IListRow } from "./List.Props"; /** * The IFixedHeightListCellDetails is used to describe the elements and indexes for a given cell. * The header row uses rowIndex = -1. */ export interface IFixedHeightListCellDetails { cellElement: HTMLElement | null; cellIndex: number; rowElement: HTMLElement | null; rowIndex: number; } /** * An IFixedHeightListRow is used to communicate details about a given row in a list. */ export interface IFixedHeightListRow extends IIndexed { /** * Data that represents the instance of T for this row. In some cases the * row data may not be available. */ data: T; } /** * The FixedHeightList uses an IFixedHeightListSelection to manage the selection state for the * rows within the list. This is required for a multi-select list and optional * for single select lists. For single select the caller can just use the * onSelect property to detect selection changes. */ export interface IFixedHeightListSelection extends ISelection, IBehavior<{}, ISelectableUI> { /** * Whether or not this selection will select items when they receive focus. */ selectOnFocus: boolean; } /** * Options to describe the list selection behavior. */ export interface IFixedHeightListSelectionOptions extends ISelectOptions { /** * Set to true to select items when they receive focus. * @default true */ selectOnFocus?: boolean; } export interface IFixedHeightListMaterializedStats { firstMaterialized: number; lastMaterialized: number; } /** * IFixedHeightList is the interace the FixedHeightList component exposes for use by callers. * If they ref the component they should access the FixedHeightList with this interface. */ export interface IFixedHeightList extends ISelectableUI { /** * scrollIntoView works like the element method in the browser, but instead of being * based on the element scrollIntoView will scroll the row specified by the rowIndex * into view. * * @param rowIndex The 0 based rowIndex that should be scrolled into view. * @param options These options are passed on to the underlying element. * @param onScrollComplete This delegate is called when the scrolling is complete. * This may not be immediate if the list has to materialize rows to scroll. * NOTE: The rowIndex passed to the delegate will be the requested rowIndex or * -1 if another scrollIntoView request is made before this scroll request completes. */ scrollIntoView: (rowIndex: number, options?: ScrollIntoViewOptions, onScrollComplete?: (rowIndex: number) => void) => void; /** * Returns an object with the indexes of the first and last materialized and rendered rows. * */ getStats(): IFixedHeightListMaterializedStats; } /** * An interface to group properties that get passed to the row renderer of each list row. */ export interface IFixedHeightListItemDetails { /** * Sets aria-busy on the list item element. */ ariaBusy?: boolean; /** * ariaLabel allows the caller to describe the elements contents to assistive * technology. */ ariaLabel?: string; /** * Sets aria-posinset on the div element. Defaults to the item index. */ ariaPosInSet?: number | null; /** * Amount to offset the aria-rowindex attribute of a row. This should be added * to the index to produce the aria-rowindex of the row. The common use case for this property * is accounting for the header row of a Table. Per the ARIA spec, a header row should be included in * the aria-rowcount, and provided an aria-rowindex of 1, meaning the first actual row of the table * needs an aria-rowindex of 2. * * @default 1 The default value is 1 because the aria-rowindex is 1-based instead of 0-based */ ariaRowOffset: number; /** * Sets aria-setsize on the div element. Defaults to the itemProvider length. */ ariaSetSize?: number | null; /** * The data represents the object being rendered in this row. If the caller * has asynchronous loading of rows, the data MAY be undefined while we wait * for the data to be resolved. */ data?: T; /** * An event dispatch the row MAY use to dispatch custom events to list behaviors. */ eventDispatch: IEventDispatch; /** * Properties used to render the list as a whole. */ listProps: IFixedHeightListProps; /** * The caller MUST supply the set of items to be shown through the ItemProvider. * The IItemProvider allows the caller to store their items in the form that * bests suits their needs but gives the table a well defined interface for * requesting the items. This can include async fetching of items through * observables. */ itemProvider: IItemProvider>; /** * The row MUST call onFocusItem when a row or one of the child elements of the row receives focus. */ onFocusItem: (rowIndex: number, event: React.FocusEvent) => void; /** * If true, will make cursor display as a pointer when hovering over the item otherwise default. */ singleClickActivation?: boolean; } /** * IFixedHeightListProps are used to describe the set of features used by the FixedHeightList component * and any attached behaviors. * * The list provides NO scrolling but does provide virutalization behaviors by default. * */ export interface IFixedHeightListProps { /** * The caller MUST supply the set of items to be shown through the ItemProvider. * The IItemProvider allows the caller to store their items in the form that * bests suits their needs but gives the list a well defined interface for * requesting the items. This can include async fetching of items through * observables. * * There is simple ArrayItemProvider for those that just have a set of items * they want to supply without writing a custom ItemProvider. */ itemProvider: IItemProvider>; /** * Aria label property for the list. */ ariaLabel?: string; /** * CSS className to add to the list root element. */ className?: string; /** * The index of the row that should be tabbable before the list has received focus. * @default 0 */ defaultTabbableRow?: number; /** * The caller can supply an EventDispatch to the list if it wishes to * participate it extending the behaviors. If one isn't supplied the list will * create its own dispatcher when behaviors are supplied. */ eventDispatch?: IEventDispatch; /** * focuszoneProps allows the caller to manage the how the list rows are focused. * The default focuszone if one isn't supplied is a Vertical non-cyclic focus zone. */ focuszoneProps?: IFocusZoneProps | null; /** * Unique Id for this list. */ id?: string; /** * The maximum height of the table when virtualized. Browsers have issues * rendering elements that are too large and when the FixedHeightList contains thousands * of elements, the list renders very large spacer elements to correctly * position the scroll bar. The large spacer elements cause rendering issues * across browsers. To bypass this, we need to limit how large the list can * grow to. By default this size is 1,000,000px. However, if you have multiple * items within a scrollable region, this number might need to be reduced. * For instance, if you have 5 lists that can contain a lot of rows in the * same scrollable region, you would likely want to set the max height for * each list to 200,000. Keep in mind that the smaller this number, the harder * it will be for a user to scroll with precision. * * @default 1000000 */ maxHeight?: number; /** * onActivate is called when the row is activated. Activation occurs on * the Enter keystroke or double click. * * @param event This is the event that is causing the activation. * @param listRow Details about the list row being activated. */ onActivate?: (event: React.SyntheticEvent, listRow: IListRow) => void; /** * onFocus is called when a item in the list is focused. Preventing default * on the focus event will prevent row selection from occuring even if * selectOnFocus is set to true. * * @param event This is the event that is causing the activation. * @param listRow Details about the list row being activated. */ onFocus?: (event: React.SyntheticEvent, listRow: IFixedHeightListRow) => void; /** * onSelect is called when the row is selected. Selection occurs on the * Space keystroke or click. * * @param event - This is the event that is causing the selection. * @param listRow - Details about the list row being selected. */ onSelect?: (event: React.SyntheticEvent, listRow: IFixedHeightListRow) => void; /** * Number of elements to materialize when an element is being scrolled to. This should be * roughly 1/2 the elements you expect to show on screen */ pageSize: number; /** * When a row's value is given as an ObservableValue with an undefined value, * the list will render a Loading row for the content. The default will be * a shimmer row that is semi random and matches the content. * * @param index This is the 0 based row index that should be rendered. * @param details Additional details about this row. */ renderLoadingRow?: (rowIndex: number, details: IFixedHeightListItemDetails) => JSX.Element; /** * All callers must supply a function for rendering the list row. * * This should render what is inside a row of the list. All focus, and positioning of the * rows will be handled by the FixedHeightList itself. * * @param index This is the 0 based row index that should be rendered. * @param item This is the object that represents the current rows data. * @param details Additional details to handle aria attributes and focus. */ renderRow: (rowIndex: number, item: T, details: IFixedHeightListItemDetails) => JSX.Element; /** * role defines the aria role of the list and defaults to "listbox" */ role?: string; /** * Required height of each row. Every row in the tree must be this height and all overflow is hidden. * The FixedHeightList control uses this value to absolutely position elements within it. */ rowHeight: number; /** * A selection object can be supplied for managing the list selection. This * is not required since the list offers onSelect as a delegate. If the caller * wants multi-selction they must use an IFixedHeightListSelection that supports multi * select. Selection should be specified on mount if used and not updated to * a new object during the FixedHeightList's lifecycle. * * There is a basic FixedHeightListSelection implementation available from the FixedHeightList * component. */ selection?: IFixedHeightListSelection; /** * Should the list select a row when it is clicked? * Provides a way to turn off row-click selection when necessary, for things like * Table with singleClickActivation * * @default true */ selectRowOnClick?: boolean; /** * Using singleClickActivation will activate the item when the row is clicked. * Where setting singleClickActivation to false will require a doubleclick to * activate a given row. * * @default false */ singleClickActivation?: boolean; /** * width can be any supported css width value, either a %, px, and vw value. */ width: string; }