import * as React from "react"; import { IReadonlyObservableValue } from '../../Core/Observable'; import { IFocusZoneProps } from '../../FocusZone'; import { IIconProps } from '../../Icon'; import { IAnchorProps } from '../../Link'; import { IList, IListItemDetails, IListRow, IListSelection, ISimpleListCell } from '../../List'; import { IBehavior } from '../../Utilities/Behavior'; import { IEventDispatch } from '../../Utilities/Dispatch'; import { IItemProvider } from '../../Utilities/Provider'; /** * ITable is the interace the Table component exposes for use by callers. * If they ref the component they should access the table using this interface. */ export interface ITable extends IList { } /** * An ITableRow is used communicate details about a given row in a table. */ export interface ITableRow extends IListRow { } /** * ITableRowDetails are used to describe the details of a given row in the table. * This information is used in rendering rows. */ export interface ITableRowDetails extends IListItemDetails { /** * css class name to add to the rendered row element. */ className?: string; /** * 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>; /** * renderSpacer can be supplied to override the spacer columns before and * after the actual columns in the table. This is used to apply custom * semantics to these areas. The standard spacers should be used unless there * is a specific need otherwise. */ renderSpacer?: (rowIndex: number, left: boolean) => React.ReactNode | null; /** * Sets the aria role on the tr element. */ role?: string; /** * A selection object can be supplied for managing the table selection. This * is not required since the table offers onSelect as a delegate. If the caller * wants multi-selction they must use an IListSelection that supports multi * select. */ selection?: IListSelection; /** * 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. */ singleClickActivation?: boolean; } /** * TabelRowRenderer function is used to render rows within a table. * * @param index The zero based index of the row being rendered. * @param item The object that represents the row being rendered. * @param details This contains both row and table properties that should be used * to render the row and its children appropriately. */ export declare type TableRowRenderer = (index: number, item: T, details: ITableRowDetails) => JSX.Element; /** * ITableProps are used to render of Table where each row is represented * by type T. * * If the object for a given row implements the renderRow function, this * function is used instead of the table scoped renderRow function. If no * renderRow function is available from the item or from the table scope * the default TableRow component will be used. */ export interface ITableProps { /** * columns is a required property of a table used to define the column layout * for the table. */ columns: Array>; /** * 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. * * There is simple ArrayItemProvider for those that just have a set of items * they want to supply without writing a custom ItemProvider. */ itemProvider: IItemProvider>; /** * Behaviors can be added to the table and monitor events and interact with the * component through the ITable methods. */ behaviors?: Array, ITable>>; /** * CSS className to add to the table root element. */ className?: string; /** * CSS className to add to the container element. */ containerClassName?: string; /** * The caller can supply an EventDispatch to the table if it wishes to * participate it extending the behaviors. If one isn't supplied the table will * create its own dispatcher when behaviors are supplied. */ eventDispatch?: IEventDispatch; /** * focuszoneProps allows the caller to manage the how the table rows are focused. * The default focuszone if one isn't supplied is a Vertical non-cyclic focus zone. */ focuszoneProps?: IFocusZoneProps | null; /** * The maximum height of the table when virtualized. Browsers have issues * rendering elements that are too large and when the List 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 100,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 20,000. Keep in mind that the smaller this number, the harder * it will be for a user to scroll with precision. * * @default 100000 */ maxHeight?: number; /** * showHeader determines whether or not the column headers are shown at the top * of the table. * * @default true */ showHeader?: boolean; /** * showLines determines whether or not lines displayed between rows. * * @default true */ showLines?: boolean; /** * Unique Id for this table. */ id?: string; /** * pageSize controls the granularity of row rendering. The table always renders * a full page worth of rows even when they are not needed to fill the viewport. * * Smaller values will help reduce the number of wasted rows that are rendered * outside the viewport, but will force the table to re-render more often as * scrolling occurs. * * @default 10 */ pageSize?: number; /** * Tables MAY render a header above the table contents. This is done through the * onRenderHeader. Like the renderRow method, there are set of restrictions the * implmentation MUST follow. * * Requirements: * * 1) The header function MUST return a singlely rooted component that resolves * to single rooted element, or return a single element that is either a * or another acceptable tag that is marked as a display: table-row. * * 2) The header function MUST only include direct child elements that are either * 's or acceptable elements that are maked as display: table-cell. The * renderer MUST return one element for each defined column even if there is * no data to be rendered in the column. */ renderHeader?: (columns: Array>) => JSX.Element; /** * 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?: (index: number, details: ITableRowDetails) => JSX.Element; /** * The requirements of this function are quite complicated and before writing * a new row renderer you should ensure one doesnt already exist that solves * your needs. * * Requirements: * * 1) The row function MUST return a singlely rooted component that resolves * to single rooted element, or return a single element that is either a * or another acceptable tag that is marked as a display: table-row. * * 2) The row function MUST only include direct child elements that are either * 's or acceptable elements that are maked as display: table-cell. The * renderer MUST return one element for each defined column even if there is * no data to be rendered in the column. * * 3) The row function MUST call onFocusItem when a either the row element of * any of the rows child elements receive the focus. This is needed to ensure * navigation within the table as well as in and out of the table function * correctly. * * 4) The row function MUST ensure the className for each of the columns is * added the cell for the given colmn. * * 5) The row function MAY dispatch events for behaviors but is not required. * If any events are dispatched they should be documented on the row renderer. * * 6) The row function is responsible for all accessibility and focus * management within the row. * */ renderRow?: TableRowRenderer; /** * renderSpacer can be supplied to override the spacer columns before and * after the actual columns in the table. This is used to apply custom * semantics to these areas. The standard spacers should be used unless there * is a specific need otherwise. * * @param rowIndex - The index of the row to render for. * @param left - True if this is the left spacer, false if it is the right spacer */ renderSpacer?: (rowIndex: number, left: boolean) => React.ReactNode | null; /** * 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 tableRow - Details about the table row being activated. */ onActivate?: (event: React.SyntheticEvent, tableRow: ITableRow) => 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 tableRow Details about the list row being activated. */ onFocus?: (event: React.SyntheticEvent, listRow: ITableRow) => 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 tableRow - Details about the table row being selected. */ onSelect?: (event: React.SyntheticEvent, tableRow: ITableRow) => void; /** * role defines the aria role of the table and defaults to "grid" * If the table does not have any focusable elements within the rows, set the role as "table" instead of "grid" * * @default "grid" */ role?: string; /** * If the caller has variable height rows they can specify the rowHeight they * want used to estimate the size of virtualized rows. This means that when rows * are not rendered, the component will create virtual space for those rows to * ensure the scrolling behavior acts appropriately. * * If the table has fixed size rows there is no need to specify a rowHeight. The * table will determine the height of the rows after the initial render when * the observer reports on page visibility. * * Question: How do I determine the rowHeight if their are variable height rows. * This one is a tough question, and the general answer is come up with a fair * average for the rows on a given page. If the select too large or too small * scrolling behaviors can become a bit odd, generally select on the smaller * side if you are unsure. */ rowHeight?: number; /** * scrollable should be set to true if the table is not contained in a * scrolling element. This will ensure the table scrolls vertically within * the table element itself. */ scrollable?: boolean; /** * A selection object can be supplied for managing the table selection. This * is not required since the table offers onSelect as a delegate. If the caller * wants multi-selction they must use an IListSelection that supports multi * select. * * There is a basic ListSelection implementation available from the List * component. */ selection?: IListSelection; /** * 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 true */ singleClickActivation?: boolean; /** * Determines the width of the spacer cells on either side of each row. */ spacerWidth?: number; /** * A table can be defined with a set of breakpoints. These breakpoints will be * used to control the column layout as the space available to the table changes. */ tableBreakpoints?: ITableBreakpoint[]; /** * If virtualize false is supplied the list will render all the items supplied * to it. This shouldn't be used unless you know you have a limited number of * rows. Virtualization is used to avoid performance problems. */ virtualize?: boolean; } /** * TableColumnLayout is used to define the general shape of the data for a given * column. One of the purposes of this is an animation when the rows are loaded * asynchronously. * * If the caller wants non-standard shapes a custom loading row function will need * to be implemented. For any columns that fit the standard shapes the exported * functions can be used. */ export declare const enum TableColumnLayout { /** * If a column is noted as none, when an asynchronous row is loaded no * animation will be added to this column. */ none = 0, /** * The row uses a single line of text. This is the default for a column that * doesnt explicitly define a column layout */ singleLine = 1, /** * The row uses a single line of text with a small prefix. */ singleLinePrefix = 2, /** * The row uses two lines of text. */ twoLine = 3, /** * The row uses two lines of text with a large prefex. */ twoLinePrefix = 4 } /** * The ColumnStyles effect how the values for the column should be rendered. */ export declare const enum TableColumnStyle { /** * Secondary colums should be rendered normally. */ Secondary = 1, /** * Primary columns should be rendered with emphasis. */ Primary = 2, /** * Tertiary columns should be rendered de-emphasized. */ Tertiary = 3 } export interface ITableColumnBehaviorProps { tableProps: Partial>; columnIndex: number; } /** * Sorting order for columns */ export declare const enum SortOrder { ascending = 0, descending = 1 } /** * Within a table columns may optionally be sorted. The table will render an indicator * in the header by default for the current sort order. SortProps should be supplied * for each column that MAY be sorted even if it isn't currently sorted. This allows * the table to know which columns are and which are not sortable. */ export interface IColumnSortProps { /** * When the sort order is ascending, the ariaLabelAscending is applied to the icon. */ ariaLabelAscending?: string; /** * When the sort order is descending, the ariaLabelDescending is applied to the icon. */ ariaLabelDescending?: string; /** * If a column is tagged as sorted, the header will indicate the sort order with * a visual icon in the if the header uses the standard renderer. If a custom * renderer is used, it should handle the sortOrder. */ sortOrder?: SortOrder; } /** * An ITableBreakpoint is used to define the layout of the columns when a given * breakpoint it reached. */ export interface ITableBreakpoint { /** * The table is defined with series of breakpoints. Each of the breakpoints * will have a mapping for the column widths. Columns widths should be * defined the same here as within an ITableColumn, except 0 should be used * for columns that are hidden at this breakpoint. */ breakpoint: number; /** * The width of each column in the table when this breakpoint is active. * The width can be a positive number for a fixed width, a negative number * for a proportional width, or 0 for a hidden column. */ columnWidths: number[]; } /** * ITableColumn is used to communicate details about the column, its layout * styles, widths, and basic header information. It requires a function to * render the contents of the cell be supplied. */ export interface ITableColumn { /** * Behaviors can be added a column to monitor events and interact with the column. */ behaviors?: Array, {}>>; /** * CSS className to that should be added to the table cell for each of the * values in this column. */ className?: string; /** * This is used to help the table understand the general layout of the data. * One of the general purposes of this value is used when data is loaded * asynchronously the table will render a loading animation by default and * the layout is used to help define the visuals for the animation. * * A column with no columnLayout defined will use the TableColumnLayout.singleLine * style. */ columnLayout?: TableColumnLayout; /** * cell renders MAY use the column style to apply css or other behaviors to * this column. */ columnStyle?: TableColumnStyle; /** * CSS className that will be added to the cell for each header element. */ headerClassName?: string; /** * If iconProps are supplied the Icon will be drawn to the left of the * column name in the header cell. */ iconProps?: IIconProps; /** * Unique Id for this table. NOTE: If this column uses the renderSimpleCell function * the id is used as the property name of the row object to render. */ id: string; /** * Mark as readonly if there are no interactive elements in the column * * @default false */ readonly?: boolean; /** * maxWidth defines how large the column MAY grow if the column is resizble. * This must be an absolute number, it can't be supplied as a percentage. */ maxWidth?: number; /** * minWidth defines how small the column MAY shrink if the column is resizble. * This must be an absolute number, it can't be supplied as a percentage. */ minWidth?: number; /** * The name of the column is used to render the column header string. If the * column has no header shown or only an icon the name can be omitted. */ name?: string; /** * renderCell MUST be supplied for a column. This defines how the column values * will be rendered in the table. Row rendering functions should use this * function unless the row rendering function has a custom presentation for * the row/column. * * @param rowIndex - This is the 0 based row index. * @param columnIndex - This is the 0 based column index. * @param tableColumn - This is the column definition for this cell. * @param tableItem - This is the object being rendered in this row. */ renderCell: (rowIndex: number, columnIndex: number, tableColumn: ITableColumn, tableItem: T) => JSX.Element; /** * renderHeaderCell can be supplied to render a custom header. It will be * the responsibility of the custom render function to manage all status * of the header. This includes all accessibitlity and focus management. * * @param index - This is the 0 based column index. * @param tableColumn - This is the column definition for the header being rendered. * @param focuszoneId - Focuszone id that needs to be included if the header is focusable. */ renderHeaderCell?: (columnIndex: number, tableColumn: ITableColumn, focuszoneId: string | undefined) => JSX.Element; /** * onSize is triggered when the column header is sized by the user. This is * a required property to enable the column to be resized. * * @param event - This is the mouse event that has caused the resize to occur. * @param index - This is the 0 based column index being resized. * @param width - This is the updated width of the column. Note: this is called * with values that conform to the defined min/max values. * @param column - This is the column definition for the column being resized. */ onSize?: (event: MouseEvent, columnIndex: number, width: number, column: ITableColumn) => void; /** * onSizeEnd is called when the currently active sizing operation has completed. * This means the user has stopped sizing the column. Any actions the consumer * wants to take when sizing ends, they should implement here. */ onSizeEnd?: () => void; /** * Within a table columns may optionally be sorted. The table will render an indicator * in the header by default for the current sort order. SortProps should be supplied * for each column that MAY be sorted even if it isn't currently sorted. This allows * the table to know which columns are and which are not sortable. */ sortProps?: IColumnSortProps; /** * The width of a column can one of two values: * * Positive value - This is the exact width of the column in pixels. This * would be something like 250 to have a 250px column. * * Negative value - This is the percentage of the remaining space in the * containing element this column should consume. This would be something * like -100 for 100% of the remaining space. * * NOTE: This is different than css since we dont want to do unneeded * string processing, CSS would have you use a string like 250px or 100%. */ width: IReadonlyObservableValue | number; } /** * Table rendering interfaces follow: */ /** * ITableHeaderCellProps are used byt he standard TableHeaderCell component to * render the header cells. If a caller wishes to use the standard header cell * component they should supply these properties. */ export interface ITableHeaderCellProps { /** * column is the core information used to describe a table column. */ column: ITableColumn; /** * columnIndex is used to define the index of this column. */ columnIndex: number; /** * An optional focuszoneId that should be added to the component when supplied. * This allows the table to provider keyboard accessibility to the header * through a focuszone. */ focuszoneId?: string; } /** * Props that can be used to represent that data available to a custom row * rendering component. * * See ITableProps.renderRow for parameter details. */ export interface ITableRowProps { /** * css class names that should be added to the row element. */ className?: string; /** * Index of the row that should be rendered. */ index: number; /** * Details about how the table is rendering rows. */ details: ITableRowDetails; /** * If the table row should be rendered as a link, the caller can supply the * links properties. These are merged with the lists properties to build * a list row that is a anchor instead of a table row. * The only props which are forwarded are href, rel, and target. */ linkProps?: IAnchorProps; } /** * If the table uses renderSimpleCell to render a given cell in the table, the * object supplied to the table should have ISimpleTableCell properties that * match the id's in the column definitions. */ export interface ISimpleTableCell { [prop: string]: ISimpleListCell | string | number; }