@jetbrains/ring-ui 7.0.124 → 8.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/babel.config.js +1 -9
- package/components/auth-dialog-service/auth-dialog-service.js +2 -2
- package/components/avatar/avatar-info.js +0 -2
- package/components/avatar/avatar-size.d.ts +0 -4
- package/components/avatar/avatar-size.js +0 -4
- package/components/avatar/avatar.js +0 -6
- package/components/avatar/fallback-avatar.js +0 -28
- package/components/avatar-stack/avatar-stack.js +1 -1
- package/components/button/button.css +4 -4
- package/components/button/button.d.ts +0 -4
- package/components/button/button.js +2 -7
- package/components/button-group/button-group.css +23 -23
- package/components/button-toolbar/button-toolbar.css +2 -2
- package/components/checkbox/checkbox.d.ts +1 -1
- package/components/collapse/collapse-content.js +7 -16
- package/components/collapse/collapse-context.d.ts +1 -0
- package/components/collapse/collapse-context.js +1 -0
- package/components/collapse/collapse-control.js +2 -2
- package/components/collapse/collapse.css +8 -2
- package/components/collapse/collapse.js +10 -2
- package/components/collapsible-group/collapsible-group.css +1 -1
- package/components/collapsible-group/collapsible-group.d.ts +5 -1
- package/components/collapsible-group/collapsible-group.js +13 -22
- package/components/confirm-service/confirm-service.js +2 -2
- package/components/contenteditable/contenteditable.js +1 -2
- package/components/data-list/data-list.d.ts +4 -4
- package/components/data-list/data-list.js +2 -2
- package/components/data-list/data-list.mock.d.ts +1 -1
- package/components/data-list/item.d.ts +1 -1
- package/components/data-list/selection.d.ts +1 -1
- package/components/data-list/selection.js +1 -1
- package/components/date-picker/month.d.ts +0 -2
- package/components/date-picker/month.js +5 -5
- package/components/date-picker/months.js +8 -7
- package/components/date-picker/use-scroll-behavior.js +5 -6
- package/components/date-picker/years.js +11 -10
- package/components/dialog/dialog.d.ts +2 -2
- package/components/dialog/dialog.js +2 -2
- package/components/dropdown-menu/dropdown-menu.d.ts +4 -4
- package/components/dropdown-menu/dropdown-menu.js +4 -4
- package/components/editable-heading/editable-heading.d.ts +1 -2
- package/components/editable-heading/editable-heading.js +5 -6
- package/components/global/compose-refs.d.ts +2 -1
- package/components/global/compose-refs.js +24 -8
- package/components/global/configuration.d.ts +0 -5
- package/components/global/configuration.js +0 -7
- package/components/global/create-stateful-context.js +5 -5
- package/components/global/focus-with-temporary-tabindex.d.ts +11 -0
- package/components/global/focus-with-temporary-tabindex.js +21 -0
- package/components/global/intersection-observer-context.d.ts +46 -0
- package/components/global/intersection-observer-context.js +91 -0
- package/components/global/is-within-interactive-element.d.ts +6 -0
- package/components/global/is-within-interactive-element.js +26 -0
- package/components/global/is-within-navigable-element.d.ts +6 -0
- package/components/global/is-within-navigable-element.js +27 -0
- package/components/global/parse-css-duration.d.ts +5 -0
- package/components/global/parse-css-duration.js +13 -0
- package/components/global/rerender-hoc.d.ts +4 -2
- package/components/global/rerender-hoc.js +4 -4
- package/components/global/schedule-with-cleanup.d.ts +12 -0
- package/components/global/schedule-with-cleanup.js +34 -0
- package/components/{table/selection.d.ts → global/table-selection.d.ts} +15 -15
- package/components/{table/selection.js → global/table-selection.js} +3 -3
- package/components/global/theme.d.ts +4 -3
- package/components/global/theme.js +8 -8
- package/components/global/variables.css +0 -10
- package/components/global/variables.interface.d.ts +0 -4
- package/components/global/variables_dark.css +0 -7
- package/components/i18n/i18n-context.js +1 -1
- package/components/icon/icon-svg.js +2 -3
- package/components/icon/icon.css +0 -27
- package/components/icon/icon.d.ts +0 -1
- package/components/icon/icon.js +2 -4
- package/components/input/input.d.ts +1 -1
- package/components/island/adaptive-island-hoc.js +4 -4
- package/components/island/content.d.ts +7 -2
- package/components/island/content.js +5 -5
- package/components/{table → legacy-table}/cell.js +1 -1
- package/components/{table → legacy-table}/header-cell.js +1 -1
- package/components/{table → legacy-table}/header.js +1 -1
- package/components/legacy-table/legacy-table.css +260 -0
- package/components/{table → legacy-table}/multitable.d.ts +1 -1
- package/components/{table → legacy-table}/row.d.ts +1 -1
- package/components/{table → legacy-table}/row.js +1 -1
- package/components/legacy-table/selection-adapter.d.ts +6 -0
- package/components/{table → legacy-table}/selection-shortcuts-hoc.d.ts +5 -5
- package/components/{table → legacy-table}/simple-table.d.ts +2 -2
- package/components/{table → legacy-table}/simple-table.js +3 -3
- package/components/{table → legacy-table}/smart-table.d.ts +5 -5
- package/components/{table → legacy-table}/smart-table.js +3 -3
- package/components/legacy-table/table.d.ts +109 -0
- package/components/legacy-table/table.js +191 -0
- package/components/list/consts.d.ts +0 -1
- package/components/list/consts.js +0 -1
- package/components/list/list.d.ts +0 -4
- package/components/login-dialog/service.js +2 -2
- package/components/popup/popup.target.d.ts +3 -2
- package/components/popup/popup.target.js +4 -6
- package/components/query-assist/query-assist.d.ts +3 -1
- package/components/query-assist/query-assist.js +2 -2
- package/components/radio/radio-item.d.ts +3 -3
- package/components/radio/radio-item.js +3 -4
- package/components/radio/radio.d.ts +2 -2
- package/components/radio/radio.js +1 -1
- package/components/select/select-popup.d.ts +3 -2
- package/components/select/select-popup.js +3 -3
- package/components/select/select.d.ts +6 -2
- package/components/select/select.js +2 -4
- package/components/selection-toolbar/selection-toolbar.css +86 -0
- package/components/selection-toolbar/selection-toolbar.d.ts +11 -0
- package/components/selection-toolbar/selection-toolbar.js +23 -0
- package/components/slider/slider.js +4 -5
- package/components/tab-trap/tab-trap.d.ts +3 -3
- package/components/tab-trap/tab-trap.js +3 -5
- package/components/table/default-item-renderer.d.ts +55 -0
- package/components/table/default-item-renderer.js +77 -0
- package/components/table/internal/reorder-animation-context.d.ts +21 -0
- package/components/table/internal/reorder-animation-context.js +60 -0
- package/components/table/internal/reorder-handle.d.ts +9 -0
- package/components/table/internal/reorder-handle.js +334 -0
- package/components/table/internal/reorder-layout-context.d.ts +16 -0
- package/components/table/internal/reorder-layout-context.js +69 -0
- package/components/table/internal/table-header.d.ts +1 -0
- package/components/table/internal/table-header.js +143 -0
- package/components/table/internal/virtualization.d.ts +41 -0
- package/components/table/internal/virtualization.js +186 -0
- package/components/table/item-virtualization.d.ts +37 -0
- package/components/table/item-virtualization.js +26 -0
- package/components/table/reorder-animation.d.ts +37 -0
- package/components/table/reorder-animation.js +11 -0
- package/components/table/reorder-item-layout.d.ts +32 -0
- package/components/table/reorder-item-layout.js +23 -0
- package/components/table/table-const.d.ts +10 -0
- package/components/table/table-const.js +10 -0
- package/components/table/table-primitives.d.ts +64 -0
- package/components/table/table-primitives.js +34 -0
- package/components/table/table-props.d.ts +304 -0
- package/components/table/table-props.js +1 -0
- package/components/table/table.css +156 -167
- package/components/table/table.d.ts +231 -107
- package/components/table/table.js +319 -183
- package/components/tags-input/tags-input.d.ts +3 -1
- package/components/tooltip/tooltip.d.ts +2 -1
- package/components/tooltip/tooltip.js +6 -5
- package/components/upload/upload.d.ts +4 -3
- package/components/upload/upload.js +3 -7
- package/components/user-agreement/service.js +2 -2
- package/components/util-stories.d.ts +26 -0
- package/components/util-stories.js +61 -0
- package/package.json +45 -48
- package/components/content-layout/content-layout.css +0 -109
- package/components/content-layout/content-layout.d.ts +0 -20
- package/components/content-layout/content-layout.js +0 -40
- package/components/content-layout/sidebar.d.ts +0 -26
- package/components/content-layout/sidebar.js +0 -71
- package/components/date-picker/use-intersection-observer.d.ts +0 -6
- package/components/date-picker/use-intersection-observer.js +0 -48
- package/components/expand/collapsible-group.css +0 -76
- package/components/expand/collapsible-group.d.ts +0 -8
- package/components/expand/collapsible-group.js +0 -14
- package/components/global/composeRefs.d.ts +0 -6
- package/components/global/composeRefs.js +0 -7
- package/components/global/use-event-callback.d.ts +0 -4
- package/components/global/use-event-callback.js +0 -20
- package/components/grid/col.d.ts +0 -16
- package/components/grid/col.js +0 -35
- package/components/grid/grid.css +0 -920
- package/components/grid/grid.d.ts +0 -13
- package/components/grid/grid.js +0 -19
- package/components/grid/row.d.ts +0 -21
- package/components/grid/row.js +0 -41
- package/components/old-browsers-message/__mocks__/old-browsers-message.js +0 -1
- package/components/old-browsers-message/old-browsers-message-stop.d.ts +0 -1
- package/components/old-browsers-message/old-browsers-message-stop.js +0 -2
- package/components/old-browsers-message/old-browsers-message.css +0 -26
- package/components/old-browsers-message/old-browsers-message.d.ts +0 -5
- package/components/old-browsers-message/old-browsers-message.js +0 -86
- package/components/old-browsers-message/white-list.d.ts +0 -2
- package/components/old-browsers-message/white-list.js +0 -24
- package/components/table/selection-adapter.d.ts +0 -6
- /package/components/{table → legacy-table}/cell.d.ts +0 -0
- /package/components/{table → legacy-table}/disable-hover-hoc.d.ts +0 -0
- /package/components/{table → legacy-table}/disable-hover-hoc.js +0 -0
- /package/components/{table → legacy-table}/header-cell.d.ts +0 -0
- /package/components/{table → legacy-table}/header.d.ts +0 -0
- /package/components/{table → legacy-table}/multitable.js +0 -0
- /package/components/{table → legacy-table}/row-with-focus-sensor.d.ts +0 -0
- /package/components/{table → legacy-table}/row-with-focus-sensor.js +0 -0
- /package/components/{table → legacy-table}/selection-adapter.js +0 -0
- /package/components/{table → legacy-table}/selection-shortcuts-hoc.js +0 -0
- /package/components/{table → legacy-table}/table.examples2.json +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* eslint-disable no-nested-ternary */
|
|
2
|
+
import { use, useCallback, useRef, useState } from 'react';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import arrowDownIcon from '@jetbrains/icons/arrow-12px-down';
|
|
5
|
+
import arrowUpIcon from '@jetbrains/icons/arrow-12px-up';
|
|
6
|
+
import settingsIcon from '@jetbrains/icons/settings-12px';
|
|
7
|
+
import trashIcon from '@jetbrains/icons/trash-12px';
|
|
8
|
+
import unsortedIcon from '@jetbrains/icons/unsorted-12px';
|
|
9
|
+
import { TablePropsContext } from '../table-const';
|
|
10
|
+
import Icon from '../../icon';
|
|
11
|
+
import { isWithinInteractiveElement } from '../../global/is-within-interactive-element';
|
|
12
|
+
import { ReorderHandle } from './reorder-handle';
|
|
13
|
+
import { ReorderAnimationContext } from './reorder-animation-context';
|
|
14
|
+
import { ReorderLayoutContext, useReorderLayoutContextValue } from './reorder-layout-context';
|
|
15
|
+
import { useReorderItemLayout } from '../reorder-item-layout';
|
|
16
|
+
import styles from '../table.css';
|
|
17
|
+
export function TableHeader() {
|
|
18
|
+
const { columns, noHeader, stickyHeader, columnEditing, onColumnEditingRequest, theadClassName, theadTrClassName } = use(TablePropsContext);
|
|
19
|
+
const [localColumnEditing, setLocalColumnEditing] = useState(false);
|
|
20
|
+
const effectiveColumnEditing = columnEditing ?? localColumnEditing;
|
|
21
|
+
const toggleColumnEditing = useCallback((source) => {
|
|
22
|
+
let newColumnEditing;
|
|
23
|
+
if (columnEditing == null) {
|
|
24
|
+
newColumnEditing = !localColumnEditing;
|
|
25
|
+
setLocalColumnEditing(newColumnEditing);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
newColumnEditing = !columnEditing;
|
|
29
|
+
}
|
|
30
|
+
onColumnEditingRequest?.(newColumnEditing, source);
|
|
31
|
+
}, [columnEditing, localColumnEditing, onColumnEditingRequest]);
|
|
32
|
+
const handleTheadClick = useCallback((e) => {
|
|
33
|
+
if (window.matchMedia('(hover: none)').matches && !isWithinInteractiveElement(e.target)) {
|
|
34
|
+
toggleColumnEditing('header');
|
|
35
|
+
}
|
|
36
|
+
}, [toggleColumnEditing]);
|
|
37
|
+
const handleEditColumnsButtonClick = useCallback(() => {
|
|
38
|
+
toggleColumnEditing('edit-button');
|
|
39
|
+
}, [toggleColumnEditing]);
|
|
40
|
+
const reorderContextValue = useReorderLayoutContextValue();
|
|
41
|
+
if (noHeader)
|
|
42
|
+
return null;
|
|
43
|
+
return (
|
|
44
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events
|
|
45
|
+
<thead className={classNames(theadClassName, effectiveColumnEditing && styles.theadColumnEditing, stickyHeader && styles.stickyHeader)} onClick={handleTheadClick}>
|
|
46
|
+
<tr className={theadTrClassName}>
|
|
47
|
+
<ReorderLayoutContext value={reorderContextValue}>
|
|
48
|
+
{columns.map((column, columnIndex) => (<TableHeaderCell key={column.key} columnIndex={columnIndex} columnEditing={effectiveColumnEditing} handleEditColumnsButtonClick={handleEditColumnsButtonClick}/>))}
|
|
49
|
+
</ReorderLayoutContext>
|
|
50
|
+
</tr>
|
|
51
|
+
</thead>);
|
|
52
|
+
}
|
|
53
|
+
function TableHeaderCell({ columnIndex, columnEditing, handleEditColumnsButtonClick, }) {
|
|
54
|
+
const ref = useRef(null);
|
|
55
|
+
const { columns, columnEditButton } = use(TablePropsContext);
|
|
56
|
+
const { key, name, renderHeader, sortOrder, deletable, canReorder, thClassName } = columns[columnIndex];
|
|
57
|
+
const { reorderAnimation } = use(ReorderAnimationContext);
|
|
58
|
+
const children = renderHeader ? renderHeader() : (name ?? String(key));
|
|
59
|
+
useReorderItemLayout({
|
|
60
|
+
index: columnIndex,
|
|
61
|
+
getBounds: () => {
|
|
62
|
+
const r = ref.current?.getBoundingClientRect();
|
|
63
|
+
return { start: r?.left ?? 0, end: r?.right ?? 0 };
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
return (<th ref={ref} className={classNames(styles.headerCell, reorderAnimation?.direction === 'columns' &&
|
|
67
|
+
reorderAnimation.index === columnIndex &&
|
|
68
|
+
reorderAnimation.className, thClassName)} scope='col' aria-sort={sortOrder}>
|
|
69
|
+
<div className={styles.headerCellInnerWrapper}>
|
|
70
|
+
<div className={styles.sortAndHeader}>
|
|
71
|
+
{canReorder && <ColumnReorderHandle columnIndex={columnIndex}/>}
|
|
72
|
+
{sortOrder ? (<SortButton columnIndex={columnIndex} aria-description={`Sort order: ${sortOrder}`}>
|
|
73
|
+
{children}
|
|
74
|
+
</SortButton>) : (children)}
|
|
75
|
+
{canReorder && <ColumnReorderHandleMirror />}
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div className={styles.rightButtons}>
|
|
79
|
+
{deletable && <DeleteColumnButton columnIndex={columnIndex}/>}
|
|
80
|
+
{columnIndex === columns.length - 1 && columnEditButton && (<EditColumnsButton columnEditing={columnEditing} onClick={handleEditColumnsButtonClick}/>)}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</th>);
|
|
84
|
+
}
|
|
85
|
+
function SortButton({ columnIndex, className, children, onClick, ...restProps }) {
|
|
86
|
+
const tableProps = use(TablePropsContext);
|
|
87
|
+
const column = tableProps?.columns[columnIndex];
|
|
88
|
+
const sortOrder = column?.sortOrder;
|
|
89
|
+
const glyph = sortOrder === 'none'
|
|
90
|
+
? unsortedIcon
|
|
91
|
+
: sortOrder === 'ascending'
|
|
92
|
+
? arrowUpIcon
|
|
93
|
+
: sortOrder === 'descending'
|
|
94
|
+
? arrowDownIcon
|
|
95
|
+
: undefined;
|
|
96
|
+
const handleClick = useCallback((e) => {
|
|
97
|
+
onClick?.(e);
|
|
98
|
+
if (!e.defaultPrevented) {
|
|
99
|
+
const nextOrder = sortOrder === 'ascending' ? 'descending' : sortOrder === 'descending' ? 'none' : 'ascending';
|
|
100
|
+
tableProps.onSort?.(columnIndex, nextOrder, tableProps.columns);
|
|
101
|
+
}
|
|
102
|
+
}, [columnIndex, onClick, sortOrder, tableProps]);
|
|
103
|
+
if (!tableProps || !column) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return (<button type='button' className={classNames(styles.tableButton, className)} onClick={handleClick} {...restProps}>
|
|
107
|
+
{children} <Icon glyph={glyph} aria-hidden/>
|
|
108
|
+
</button>);
|
|
109
|
+
}
|
|
110
|
+
function DeleteColumnButton({ columnIndex, className, onClick, ...restProps }) {
|
|
111
|
+
const tableProps = use(TablePropsContext);
|
|
112
|
+
const column = tableProps?.columns[columnIndex];
|
|
113
|
+
const handleClick = useCallback((e) => {
|
|
114
|
+
onClick?.(e);
|
|
115
|
+
if (!e.defaultPrevented) {
|
|
116
|
+
const columns = tableProps.columns;
|
|
117
|
+
tableProps.onColumnDelete?.(columns[columnIndex], columnIndex, columns);
|
|
118
|
+
}
|
|
119
|
+
}, [columnIndex, onClick, tableProps]);
|
|
120
|
+
if (!tableProps || !column) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const hint = `Delete column ${column.name ?? String(column.key)}.`;
|
|
124
|
+
return (<button type='button' className={classNames(styles.tableButton, styles.deleteColumnButton, className)} onClick={handleClick} aria-label={hint} title={hint} {...restProps}>
|
|
125
|
+
<Icon glyph={trashIcon} aria-hidden='true'/>
|
|
126
|
+
</button>);
|
|
127
|
+
}
|
|
128
|
+
function EditColumnsButton({ columnEditing, ...props }) {
|
|
129
|
+
const { className, ...restProps } = props;
|
|
130
|
+
const hint = columnEditing ? 'Hide column controls.' : 'Show column controls.';
|
|
131
|
+
return (<button type='button' className={classNames(styles.tableButton, styles.editColumnsButton, className)} aria-pressed={columnEditing} aria-label={hint} title={hint} {...restProps}>
|
|
132
|
+
<Icon glyph={settingsIcon} aria-hidden='true'/>
|
|
133
|
+
</button>);
|
|
134
|
+
}
|
|
135
|
+
function ColumnReorderHandle({ columnIndex, ...restProps }) {
|
|
136
|
+
return <ReorderHandle direction='columns' index={columnIndex} {...restProps}/>;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Reserves the space for the reorder handle and prevents layout shift when the handle appears on hover.
|
|
140
|
+
*/
|
|
141
|
+
function ColumnReorderHandleMirror() {
|
|
142
|
+
return <span className={styles.columnReorderHandleMirror} aria-hidden='true'/>;
|
|
143
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import { type IntersectionObserverHandle } from '../../global/intersection-observer-context';
|
|
3
|
+
export type VirtualItem = MaterializedItem | Spacer;
|
|
4
|
+
interface MaterializedItem {
|
|
5
|
+
type: 'materialized';
|
|
6
|
+
index: number;
|
|
7
|
+
}
|
|
8
|
+
interface Spacer {
|
|
9
|
+
type: 'spacer';
|
|
10
|
+
from: number;
|
|
11
|
+
to: number;
|
|
12
|
+
height: number;
|
|
13
|
+
key: string;
|
|
14
|
+
}
|
|
15
|
+
type CollapseItemIntoSpacerCallback = (index: number, height: number) => void;
|
|
16
|
+
interface VirtualizationContextValue {
|
|
17
|
+
intersectionObserverHandle: IntersectionObserverHandle;
|
|
18
|
+
collapseItemIntoSpacer: CollapseItemIntoSpacerCallback;
|
|
19
|
+
}
|
|
20
|
+
export declare const VirtualizationContext: import("react").Context<VirtualizationContextValue>;
|
|
21
|
+
export declare function useVirtualItems<T>({ enabled, data, data: { length }, scrollerRef, tableRef, estimateHeight, lookaheadPx, retentionMarginPx, minScrollAndResizeDeltaPx, }: {
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
data: readonly T[];
|
|
24
|
+
scrollerRef: RefObject<HTMLElement | null> | undefined;
|
|
25
|
+
tableRef: RefObject<HTMLTableElement | null>;
|
|
26
|
+
estimateHeight: (item: T, index: number, items: readonly T[]) => number;
|
|
27
|
+
lookaheadPx: number;
|
|
28
|
+
retentionMarginPx: number;
|
|
29
|
+
minScrollAndResizeDeltaPx: number;
|
|
30
|
+
}): {
|
|
31
|
+
virtualItems: VirtualItem[];
|
|
32
|
+
virtualizationContextValue: {
|
|
33
|
+
intersectionObserverHandle: IntersectionObserverHandle;
|
|
34
|
+
collapseItemIntoSpacer: (index: number, height: number) => void;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export declare function SpacerRow({ spacer: { from, to, height }, colSpan }: {
|
|
38
|
+
spacer: Spacer;
|
|
39
|
+
colSpan: number;
|
|
40
|
+
}): import("react").JSX.Element;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { createContext, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useIntersectionObserverHandle, } from '../../global/intersection-observer-context';
|
|
3
|
+
import { setTimeoutWithCleanup } from '../../global/schedule-with-cleanup';
|
|
4
|
+
import styles from '../table.css';
|
|
5
|
+
export const VirtualizationContext = createContext({
|
|
6
|
+
intersectionObserverHandle: { observe: () => () => { } },
|
|
7
|
+
collapseItemIntoSpacer: () => { },
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* RAF is somewhat too frequent. Most updates happen on virtualization boundaries
|
|
11
|
+
* within the invisible overscroll area, so they don't require such a high update rate.
|
|
12
|
+
* Therefore, we throttle with a custom delay.
|
|
13
|
+
*/
|
|
14
|
+
const virtualizationThrottleDelay = 50;
|
|
15
|
+
export function useVirtualItems({ enabled, data, data: { length }, scrollerRef, tableRef, estimateHeight, lookaheadPx, retentionMarginPx, minScrollAndResizeDeltaPx, }) {
|
|
16
|
+
const itemsMaterialization = useRef([]);
|
|
17
|
+
const [virtualItems, setVirtualItems] = useState(() => enabled ? getAllVirtualizedItems(data, estimateHeight) : []);
|
|
18
|
+
const materializeVisibleSpacerItems = useCallback(() => {
|
|
19
|
+
if (!tableRef.current)
|
|
20
|
+
return;
|
|
21
|
+
const scrollerRect = scrollerRef?.current?.getBoundingClientRect();
|
|
22
|
+
const visibleStart = scrollerRect?.top ?? 0;
|
|
23
|
+
const visibleEnd = scrollerRect ? Math.min(scrollerRect.bottom, window.innerHeight) : window.innerHeight;
|
|
24
|
+
const materializeStart = visibleStart - lookaheadPx;
|
|
25
|
+
const materializeEnd = visibleEnd + lookaheadPx;
|
|
26
|
+
for (const spacerRow of tableRef.current.querySelectorAll(`.${styles.spacerRow}`)) {
|
|
27
|
+
const spacerRect = spacerRow.getBoundingClientRect();
|
|
28
|
+
if (spacerRect.bottom < materializeStart || spacerRect.top > materializeEnd) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const from = Number(spacerRow.dataset.from);
|
|
32
|
+
const to = Number(spacerRow.dataset.to);
|
|
33
|
+
let currentTop = spacerRect.top;
|
|
34
|
+
for (let i = from; i < to; i++) {
|
|
35
|
+
const itemMaterialization = itemsMaterialization.current[i];
|
|
36
|
+
const itemHeight = typeof itemMaterialization === 'number' ? itemMaterialization : estimateHeight(data[i], i, data);
|
|
37
|
+
const itemTop = currentTop;
|
|
38
|
+
const itemBottom = currentTop + itemHeight;
|
|
39
|
+
if (itemBottom >= materializeStart && itemTop <= materializeEnd) {
|
|
40
|
+
itemsMaterialization.current[i] = true;
|
|
41
|
+
}
|
|
42
|
+
else if (itemTop > materializeEnd) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
currentTop += itemHeight;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}, [data, estimateHeight, lookaheadPx, scrollerRef, tableRef]);
|
|
49
|
+
const recomputeVirtualItems = useCallback(() => {
|
|
50
|
+
const newVirtualItems = [];
|
|
51
|
+
let spacerCounter = 0;
|
|
52
|
+
for (let i = 0; i < length; i++) {
|
|
53
|
+
const itemMaterialization = itemsMaterialization.current[i];
|
|
54
|
+
if (itemMaterialization === true) {
|
|
55
|
+
newVirtualItems.push({ type: 'materialized', index: i });
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const lastItemOrSpacer = newVirtualItems[newVirtualItems.length - 1];
|
|
59
|
+
const lastSpacer = lastItemOrSpacer?.type === 'spacer' ? lastItemOrSpacer : undefined;
|
|
60
|
+
const height = typeof itemMaterialization === 'number' ? itemMaterialization : estimateHeight(data[i], i, data);
|
|
61
|
+
if (lastSpacer) {
|
|
62
|
+
lastSpacer.to = i + 1;
|
|
63
|
+
lastSpacer.height += height;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
newVirtualItems.push({
|
|
67
|
+
type: 'spacer',
|
|
68
|
+
from: i,
|
|
69
|
+
to: i + 1,
|
|
70
|
+
height,
|
|
71
|
+
key: spacerKey(spacerCounter++),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
setVirtualItems(newVirtualItems);
|
|
77
|
+
}, [data, estimateHeight, length]);
|
|
78
|
+
const timerIdRef = useRef(null);
|
|
79
|
+
const callbacksRef = useRef(null);
|
|
80
|
+
const throttle = useCallback((...callbacks) => {
|
|
81
|
+
if (timerIdRef.current != null) {
|
|
82
|
+
callbacks.forEach(cb => {
|
|
83
|
+
callbacksRef.current.delete(cb);
|
|
84
|
+
callbacksRef.current.add(cb);
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
callbacksRef.current = new Set(callbacks);
|
|
89
|
+
timerIdRef.current = window.setTimeout(() => {
|
|
90
|
+
callbacksRef.current.forEach(cb => cb());
|
|
91
|
+
timerIdRef.current = null;
|
|
92
|
+
callbacksRef.current = null;
|
|
93
|
+
}, virtualizationThrottleDelay);
|
|
94
|
+
}, []);
|
|
95
|
+
const prevEnabledRef = useRef(enabled);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const prevEnabled = prevEnabledRef.current;
|
|
98
|
+
prevEnabledRef.current = enabled;
|
|
99
|
+
if (!enabled)
|
|
100
|
+
return;
|
|
101
|
+
const scroller = scrollerRef?.current;
|
|
102
|
+
let lastHandledScrollTop = -Infinity;
|
|
103
|
+
let lastHandledHeight = -Infinity;
|
|
104
|
+
let lastHandledWidth = -Infinity;
|
|
105
|
+
function handleViewportChange() {
|
|
106
|
+
const scrollTop = scroller ? scroller.scrollTop : window.scrollY;
|
|
107
|
+
const height = scroller ? scroller.clientHeight : window.innerHeight;
|
|
108
|
+
const width = scroller ? scroller.clientWidth : window.innerWidth;
|
|
109
|
+
if (Math.abs(scrollTop - lastHandledScrollTop) >= minScrollAndResizeDeltaPx ||
|
|
110
|
+
Math.abs(height - lastHandledHeight) >= minScrollAndResizeDeltaPx ||
|
|
111
|
+
Math.abs(width - lastHandledWidth) >= minScrollAndResizeDeltaPx) {
|
|
112
|
+
lastHandledScrollTop = scrollTop;
|
|
113
|
+
lastHandledHeight = height;
|
|
114
|
+
lastHandledWidth = width;
|
|
115
|
+
throttle(materializeVisibleSpacerItems, recomputeVirtualItems);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const scrollTarget = scroller ?? window;
|
|
119
|
+
scrollTarget.addEventListener('scroll', handleViewportChange, { passive: true });
|
|
120
|
+
const resizeObserver = new ResizeObserver(handleViewportChange);
|
|
121
|
+
const resizeTarget = scroller ?? document.documentElement;
|
|
122
|
+
resizeObserver.observe(resizeTarget);
|
|
123
|
+
let timeoutCleanup;
|
|
124
|
+
if (!prevEnabled) {
|
|
125
|
+
timeoutCleanup = setTimeoutWithCleanup(() => {
|
|
126
|
+
itemsMaterialization.current = [];
|
|
127
|
+
setVirtualItems(getAllVirtualizedItems(data, estimateHeight));
|
|
128
|
+
handleViewportChange();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
handleViewportChange();
|
|
133
|
+
}
|
|
134
|
+
return () => {
|
|
135
|
+
timeoutCleanup?.();
|
|
136
|
+
scrollTarget.removeEventListener('scroll', handleViewportChange);
|
|
137
|
+
resizeObserver.unobserve(resizeTarget);
|
|
138
|
+
resizeObserver.disconnect();
|
|
139
|
+
if (timerIdRef.current != null) {
|
|
140
|
+
window.clearTimeout(timerIdRef.current);
|
|
141
|
+
timerIdRef.current = null;
|
|
142
|
+
callbacksRef.current = null;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}, [
|
|
146
|
+
data,
|
|
147
|
+
enabled,
|
|
148
|
+
estimateHeight,
|
|
149
|
+
length,
|
|
150
|
+
materializeVisibleSpacerItems,
|
|
151
|
+
minScrollAndResizeDeltaPx,
|
|
152
|
+
recomputeVirtualItems,
|
|
153
|
+
scrollerRef,
|
|
154
|
+
throttle,
|
|
155
|
+
]);
|
|
156
|
+
const intersectionObserverHandle = useIntersectionObserverHandle(scrollerRef, scrollerRef ? retentionMarginPx : undefined, !scrollerRef ? retentionMarginPx : undefined);
|
|
157
|
+
function collapseItemIntoSpacer(index, height) {
|
|
158
|
+
if (!enabled)
|
|
159
|
+
return;
|
|
160
|
+
itemsMaterialization.current[index] = height;
|
|
161
|
+
throttle(recomputeVirtualItems);
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
virtualItems,
|
|
165
|
+
virtualizationContextValue: { intersectionObserverHandle, collapseItemIntoSpacer },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function getAllVirtualizedItems(data, estimateHeight) {
|
|
169
|
+
return [
|
|
170
|
+
{
|
|
171
|
+
type: 'spacer',
|
|
172
|
+
from: 0,
|
|
173
|
+
to: data.length,
|
|
174
|
+
height: data.reduce((acc, item, index, items) => acc + estimateHeight(item, index, items), 0),
|
|
175
|
+
key: spacerKey(0),
|
|
176
|
+
},
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
function spacerKey(index) {
|
|
180
|
+
return `__${styles.spacerRow}-${index}`;
|
|
181
|
+
}
|
|
182
|
+
export function SpacerRow({ spacer: { from, to, height }, colSpan }) {
|
|
183
|
+
return (<tr className={styles.spacerRow} data-from={from} data-to={to}>
|
|
184
|
+
<td className={styles.spacerCell} colSpan={colSpan} style={{ height }}/>
|
|
185
|
+
</tr>);
|
|
186
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Use in an item renderer to control item virtualization.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useItemVirtualization({ index, refs, onIntersectionChange, }: {
|
|
6
|
+
/**
|
|
7
|
+
* Index of the item.
|
|
8
|
+
*/
|
|
9
|
+
index: number;
|
|
10
|
+
/**
|
|
11
|
+
* One or more elements representing this item.
|
|
12
|
+
*
|
|
13
|
+
* When multiple elements are provided, the virtualization callback receives
|
|
14
|
+
* the intersection state of all of them.
|
|
15
|
+
*
|
|
16
|
+
* If you pass multiple refs, pass a stable array reference to avoid restarting
|
|
17
|
+
* observation on every render — for example, memoize it with `useMemo()`,
|
|
18
|
+
* unless you use the React Compiler.
|
|
19
|
+
*/
|
|
20
|
+
refs: RefObject<HTMLElement | null> | RefObject<HTMLElement | null>[];
|
|
21
|
+
/**
|
|
22
|
+
* Invoked when the `isIntersecting` state of the observed elements changes.
|
|
23
|
+
* Pass a stable function reference to avoid restarting observation on every render —
|
|
24
|
+
* for example, wrap it with `useCallback()`, unless you use the React Compiler.
|
|
25
|
+
*
|
|
26
|
+
* @param intersectionStates - Current intersection state of every observed element.
|
|
27
|
+
* Entries are initially `undefined` until the corresponding element
|
|
28
|
+
* has been reported by `IntersectionObserver`.
|
|
29
|
+
* @param changedIndex - Index of the element whose intersection state changed.
|
|
30
|
+
* @param elements - The observed elements. Note that some elements may
|
|
31
|
+
* already be disconnected from the DOM when this callback is invoked.
|
|
32
|
+
* Use additional checks like `element.isConnected`.
|
|
33
|
+
* @returns Return the height of an item to collapse the item into spacer,
|
|
34
|
+
* or `undefined` to keep the item rendered.
|
|
35
|
+
*/
|
|
36
|
+
onIntersectionChange: (intersectionStates: (boolean | undefined)[], changedIndex: number, elements: (Element | null)[]) => number | undefined;
|
|
37
|
+
}): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { use, useEffect } from 'react';
|
|
2
|
+
import { VirtualizationContext } from './internal/virtualization';
|
|
3
|
+
/**
|
|
4
|
+
* Use in an item renderer to control item virtualization.
|
|
5
|
+
*/
|
|
6
|
+
export function useItemVirtualization({ index, refs, onIntersectionChange, }) {
|
|
7
|
+
const { intersectionObserverHandle, collapseItemIntoSpacer } = use(VirtualizationContext);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const intersectionStates = Array.isArray(refs) ? refs.map(() => undefined) : [undefined];
|
|
10
|
+
const elements = Array.isArray(refs) ? refs.map(r => r.current) : [refs.current];
|
|
11
|
+
const cleanups = [];
|
|
12
|
+
elements.forEach((element, elementIndex) => {
|
|
13
|
+
if (!element)
|
|
14
|
+
return;
|
|
15
|
+
const cleanup = intersectionObserverHandle.observe(element, isIntersecting => {
|
|
16
|
+
intersectionStates[elementIndex] = isIntersecting;
|
|
17
|
+
const height = onIntersectionChange(intersectionStates, elementIndex, elements);
|
|
18
|
+
if (height != null) {
|
|
19
|
+
collapseItemIntoSpacer(index, height);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
cleanups.push(cleanup);
|
|
23
|
+
});
|
|
24
|
+
return () => cleanups.forEach(cleanup => cleanup());
|
|
25
|
+
}, [collapseItemIntoSpacer, intersectionObserverHandle, index, onIntersectionChange, refs]);
|
|
26
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Information about a column or item currently being animated after reorder.
|
|
3
|
+
*
|
|
4
|
+
* Available through {@link useReorderAnimation} to allow custom cell
|
|
5
|
+
* renderers to animate reordered columns and items consistently with the default
|
|
6
|
+
* table renderer.
|
|
7
|
+
*/
|
|
8
|
+
export interface ReorderAnimation {
|
|
9
|
+
/**
|
|
10
|
+
* What is animated: columns or items (rows).
|
|
11
|
+
*/
|
|
12
|
+
direction: 'columns' | 'items';
|
|
13
|
+
/**
|
|
14
|
+
* Index of the column or item being animated.
|
|
15
|
+
*/
|
|
16
|
+
index: number;
|
|
17
|
+
/**
|
|
18
|
+
* Current animation phase.
|
|
19
|
+
*/
|
|
20
|
+
phase: 'initial' | 'fade-out';
|
|
21
|
+
/**
|
|
22
|
+
* CSS class to apply to the animated row, cell, or another element used to
|
|
23
|
+
* render the animation on this phase.
|
|
24
|
+
*
|
|
25
|
+
* The class sets `background-color` and `transition`. If your custom styles
|
|
26
|
+
* also define `transition` on the same element, they may conflict with the
|
|
27
|
+
* animation.
|
|
28
|
+
*/
|
|
29
|
+
className: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Provides information about the currently animated column or item after reorder.
|
|
33
|
+
*
|
|
34
|
+
* Use in a custom cell renderer to animate reordered columns and items
|
|
35
|
+
* consistently with the default table renderer.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useReorderAnimation(): ReorderAnimation | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { use } from 'react';
|
|
2
|
+
import { ReorderAnimationContext } from './internal/reorder-animation-context';
|
|
3
|
+
/**
|
|
4
|
+
* Provides information about the currently animated column or item after reorder.
|
|
5
|
+
*
|
|
6
|
+
* Use in a custom cell renderer to animate reordered columns and items
|
|
7
|
+
* consistently with the default table renderer.
|
|
8
|
+
*/
|
|
9
|
+
export function useReorderAnimation() {
|
|
10
|
+
return use(ReorderAnimationContext).reorderAnimation;
|
|
11
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers the physical boundaries of a custom-rendered item with the reorder
|
|
3
|
+
* system, so that the insertion indicator and insertion point calculation are
|
|
4
|
+
* correct for items that span multiple rows or have non-standard sizing.
|
|
5
|
+
*
|
|
6
|
+
* `DefaultItemRenderer` calls this automatically. Use this hook in a custom item
|
|
7
|
+
* renderer when the default boundary measurement is insufficient — for example,
|
|
8
|
+
* when the item spans multiple rows, or when you don't use
|
|
9
|
+
* the `DefaultItemRenderer` at all.
|
|
10
|
+
*
|
|
11
|
+
* If you include `DefaultItemRenderer` inside your custom renderer, set its
|
|
12
|
+
* `noReorderLayout` prop to `true` to prevent double registration.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useReorderItemLayout({ disabled, index, getBounds, }: {
|
|
15
|
+
/**
|
|
16
|
+
* When `true`, the item is not registered. Use to disable without violating
|
|
17
|
+
* the rules of hooks.
|
|
18
|
+
*/
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Index of the item in the `data` array.
|
|
22
|
+
*/
|
|
23
|
+
index: number;
|
|
24
|
+
/**
|
|
25
|
+
* Returns the item's top and bottom client coordinates (in pixels, relative
|
|
26
|
+
* to the viewport).
|
|
27
|
+
*/
|
|
28
|
+
getBounds: () => {
|
|
29
|
+
start: number;
|
|
30
|
+
end: number;
|
|
31
|
+
};
|
|
32
|
+
}): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { use, useEffect } from 'react';
|
|
2
|
+
import { ReorderLayoutContext } from './internal/reorder-layout-context';
|
|
3
|
+
/**
|
|
4
|
+
* Registers the physical boundaries of a custom-rendered item with the reorder
|
|
5
|
+
* system, so that the insertion indicator and insertion point calculation are
|
|
6
|
+
* correct for items that span multiple rows or have non-standard sizing.
|
|
7
|
+
*
|
|
8
|
+
* `DefaultItemRenderer` calls this automatically. Use this hook in a custom item
|
|
9
|
+
* renderer when the default boundary measurement is insufficient — for example,
|
|
10
|
+
* when the item spans multiple rows, or when you don't use
|
|
11
|
+
* the `DefaultItemRenderer` at all.
|
|
12
|
+
*
|
|
13
|
+
* If you include `DefaultItemRenderer` inside your custom renderer, set its
|
|
14
|
+
* `noReorderLayout` prop to `true` to prevent double registration.
|
|
15
|
+
*/
|
|
16
|
+
export function useReorderItemLayout({ disabled, index, getBounds, }) {
|
|
17
|
+
const { registerReorderItem } = use(ReorderLayoutContext);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (disabled)
|
|
20
|
+
return;
|
|
21
|
+
return registerReorderItem(index, getBounds);
|
|
22
|
+
}, [index, getBounds, registerReorderItem, disabled]);
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TableProps } from './table-props';
|
|
2
|
+
/**
|
|
3
|
+
* Use anywhere inside the table to get access to the props passed to it.
|
|
4
|
+
* Cast to `Context<TableProps<T>>` in usage place.
|
|
5
|
+
*/
|
|
6
|
+
export declare const TablePropsContext: import("react").Context<TableProps<unknown> | null>;
|
|
7
|
+
/**
|
|
8
|
+
* When a row only contains unformatted single-line text, it will be exactly of this height.
|
|
9
|
+
*/
|
|
10
|
+
export declare const defaultRowHeight = 37;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Use anywhere inside the table to get access to the props passed to it.
|
|
4
|
+
* Cast to `Context<TableProps<T>>` in usage place.
|
|
5
|
+
*/
|
|
6
|
+
export const TablePropsContext = createContext(null);
|
|
7
|
+
/**
|
|
8
|
+
* When a row only contains unformatted single-line text, it will be exactly of this height.
|
|
9
|
+
*/
|
|
10
|
+
export const defaultRowHeight = 37;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef } from 'react';
|
|
2
|
+
export interface TableRowProps {
|
|
3
|
+
/**
|
|
4
|
+
* @see DefaultItemRendererProps.keyboardFocusable
|
|
5
|
+
*/
|
|
6
|
+
keyboardFocusable?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare const keyboardFocusableAttrName = "data-keyboard-focusable";
|
|
12
|
+
/**
|
|
13
|
+
* A helper `<tr>` component for custom {@link TableProps.renderItem} implementations.
|
|
14
|
+
* Applies the standard row class names.
|
|
15
|
+
*/
|
|
16
|
+
export declare function TableRow(props: TableRowProps & ComponentPropsWithRef<'tr'>): import("react").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* A helper `<td>` component for custom {@link TableProps.renderItem} implementations.
|
|
19
|
+
* Applies the standard cell class names, but not data-dependent `tdClassName`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function TableCell(props: ComponentPropsWithRef<'td'>): import("react").JSX.Element;
|
|
22
|
+
/**
|
|
23
|
+
* - `'pointerdown'` means the user has pressed the mouse button or touched the handle.
|
|
24
|
+
* - `number` means the distance in pixels the user has dragged the handle along
|
|
25
|
+
* the Y axis.
|
|
26
|
+
* - `'cancelled'` means the reorder was aborted — e.g. the user pressed Escape,
|
|
27
|
+
* released the pointer outside the browser, or dropped the item at its original
|
|
28
|
+
* position. Use this phase to play a cancellation animation in your custom renderer.
|
|
29
|
+
* This state lasts 600 ms (matching the built-in cancel animation), after which
|
|
30
|
+
* `undefined` is sent.
|
|
31
|
+
* - `undefined` means the drag interaction is fully over. Sent after a successful
|
|
32
|
+
* reorder as well as after the `'cancelled'` phase, so you can always do cleanup
|
|
33
|
+
* here without managing your own timer.
|
|
34
|
+
*/
|
|
35
|
+
export type DragState = 'pointerdown' | number | 'cancelled' | undefined;
|
|
36
|
+
export interface ItemReorderHandleProps {
|
|
37
|
+
/**
|
|
38
|
+
* The index of the item in the table data array.
|
|
39
|
+
*/
|
|
40
|
+
index: number;
|
|
41
|
+
/**
|
|
42
|
+
* When `true`, the drag frame, indicating the currently dragged item,
|
|
43
|
+
* will not be rendered.
|
|
44
|
+
*/
|
|
45
|
+
noDragFrame?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* When `true`, the drag handle stays fixed in place instead of following
|
|
48
|
+
* the pointer during drag.
|
|
49
|
+
*/
|
|
50
|
+
noHandleTranslate?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Callback that is called when the user drags the handle.
|
|
53
|
+
* Use it for custom drag indication.
|
|
54
|
+
* The `TableProps.onItemReorder` will be called after this callback.
|
|
55
|
+
* @param state The current drag state.
|
|
56
|
+
*/
|
|
57
|
+
onUserDrag?: (state: DragState) => void;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A drag handle that allows the user to reorder the row. Place it anywhere
|
|
61
|
+
* inside a row renderer.
|
|
62
|
+
* Use {@link TableProps.canReorderItem} and {@link TableProps.onItemReorder}.
|
|
63
|
+
*/
|
|
64
|
+
export declare function ItemReorderHandle({ index, ...restProps }: ItemReorderHandleProps & ComponentPropsWithRef<'button'>): import("react").JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { ReorderHandle } from './internal/reorder-handle';
|
|
3
|
+
import styles from './table.css';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export const keyboardFocusableAttrName = 'data-keyboard-focusable';
|
|
8
|
+
/**
|
|
9
|
+
* A helper `<tr>` component for custom {@link TableProps.renderItem} implementations.
|
|
10
|
+
* Applies the standard row class names.
|
|
11
|
+
*/
|
|
12
|
+
export function TableRow(props) {
|
|
13
|
+
const { keyboardFocusable, className, ...restProps } = props;
|
|
14
|
+
const classes = classNames(styles.row, className);
|
|
15
|
+
const trRestProps = keyboardFocusable ? { [keyboardFocusableAttrName]: '', ...restProps } : restProps;
|
|
16
|
+
return <tr className={classes} {...trRestProps}/>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A helper `<td>` component for custom {@link TableProps.renderItem} implementations.
|
|
20
|
+
* Applies the standard cell class names, but not data-dependent `tdClassName`.
|
|
21
|
+
*/
|
|
22
|
+
export function TableCell(props) {
|
|
23
|
+
const { className, ...restProps } = props;
|
|
24
|
+
const classes = classNames(styles.cell, className);
|
|
25
|
+
return <td className={classes} {...restProps}/>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A drag handle that allows the user to reorder the row. Place it anywhere
|
|
29
|
+
* inside a row renderer.
|
|
30
|
+
* Use {@link TableProps.canReorderItem} and {@link TableProps.onItemReorder}.
|
|
31
|
+
*/
|
|
32
|
+
export function ItemReorderHandle({ index, ...restProps }) {
|
|
33
|
+
return <ReorderHandle direction='items' index={index} {...restProps}/>;
|
|
34
|
+
}
|