124 lines
4.7 KiB
Plaintext
124 lines
4.7 KiB
Plaintext
/*
|
|
* Copyright 2020 Adobe. All rights reserved.
|
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
* governing permissions and limitations under the License.
|
|
*/
|
|
|
|
import {announce} from '@react-aria/live-announcer';
|
|
import {GridAria, GridProps, useGrid} from '@react-aria/grid';
|
|
import {gridIds} from './utils';
|
|
// @ts-ignore
|
|
import intlMessages from '../intl/*.json';
|
|
import {Key, LayoutDelegate, Rect, RefObject, Size} from '@react-types/shared';
|
|
import {mergeProps, useDescription, useId, useUpdateEffect} from '@react-aria/utils';
|
|
import {TableKeyboardDelegate} from './TableKeyboardDelegate';
|
|
import {tableNestedRows} from '@react-stately/flags';
|
|
import {TableState, TreeGridState} from '@react-stately/table';
|
|
import {useCollator, useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
|
|
import {useMemo} from 'react';
|
|
|
|
export interface AriaTableProps extends GridProps {
|
|
/** The layout object for the table. Computes what content is visible and how to position and style them. */
|
|
layoutDelegate?: LayoutDelegate,
|
|
/** @deprecated - Use layoutDelegate instead. */
|
|
layout?: DeprecatedLayout
|
|
}
|
|
|
|
interface DeprecatedLayout {
|
|
getLayoutInfo(key: Key): DeprecatedLayoutInfo,
|
|
getContentSize(): Size,
|
|
virtualizer: DeprecatedVirtualizer
|
|
}
|
|
|
|
interface DeprecatedLayoutInfo {
|
|
rect: Rect
|
|
}
|
|
|
|
interface DeprecatedVirtualizer {
|
|
visibleRect: Rect
|
|
}
|
|
|
|
/**
|
|
* Provides the behavior and accessibility implementation for a table component.
|
|
* A table displays data in rows and columns and enables a user to navigate its contents via directional navigation keys,
|
|
* and optionally supports row selection and sorting.
|
|
* @param props - Props for the table.
|
|
* @param state - State for the table, as returned by `useTableState`.
|
|
* @param ref - The ref attached to the table element.
|
|
*/
|
|
export function useTable<T>(props: AriaTableProps, state: TableState<T> | TreeGridState<T>, ref: RefObject<HTMLElement | null>): GridAria {
|
|
let {
|
|
keyboardDelegate,
|
|
isVirtualized,
|
|
layoutDelegate,
|
|
layout
|
|
} = props;
|
|
|
|
// By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down).
|
|
// When virtualized, the layout object will be passed in as a prop and override this.
|
|
let collator = useCollator({usage: 'search', sensitivity: 'base'});
|
|
let {direction} = useLocale();
|
|
let disabledBehavior = state.selectionManager.disabledBehavior;
|
|
let delegate = useMemo(() => keyboardDelegate || new TableKeyboardDelegate({
|
|
collection: state.collection,
|
|
disabledKeys: state.disabledKeys,
|
|
disabledBehavior,
|
|
ref,
|
|
direction,
|
|
collator,
|
|
layoutDelegate,
|
|
layout
|
|
}), [keyboardDelegate, state.collection, state.disabledKeys, disabledBehavior, ref, direction, collator, layoutDelegate, layout]);
|
|
let id = useId(props.id);
|
|
gridIds.set(state, id);
|
|
|
|
let {gridProps} = useGrid({
|
|
...props,
|
|
id,
|
|
keyboardDelegate: delegate
|
|
}, state, ref);
|
|
|
|
// Override to include header rows
|
|
if (isVirtualized) {
|
|
gridProps['aria-rowcount'] = state.collection.size + state.collection.headerRows.length;
|
|
}
|
|
|
|
if (tableNestedRows() && 'expandedKeys' in state) {
|
|
gridProps.role = 'treegrid';
|
|
}
|
|
|
|
let {column, direction: sortDirection} = state.sortDescriptor || {};
|
|
let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/table');
|
|
let sortDescription = useMemo(() => {
|
|
let columnName = state.collection.columns.find(c => c.key === column)?.textValue ?? '';
|
|
return sortDirection && column ? stringFormatter.format(`${sortDirection}Sort`, {columnName}) : undefined;
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [sortDirection, column, state.collection.columns]);
|
|
|
|
let descriptionProps = useDescription(sortDescription);
|
|
|
|
// Only announce after initial render, tabbing to the table will tell you the initial sort info already
|
|
useUpdateEffect(() => {
|
|
if (sortDescription) {
|
|
announce(sortDescription, 'assertive', 500);
|
|
}
|
|
}, [sortDescription]);
|
|
|
|
return {
|
|
gridProps: mergeProps(
|
|
gridProps,
|
|
descriptionProps,
|
|
{
|
|
// merge sort description with long press information
|
|
'aria-describedby': [descriptionProps['aria-describedby'], gridProps['aria-describedby']].filter(Boolean).join(' ')
|
|
}
|
|
)
|
|
};
|
|
}
|