@jetbrains/ring-ui 8.0.0-beta.3 → 8.0.0-beta.4
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/components/checkbox/checkbox.d.ts +1 -1
- package/components/collapsible-group/collapsible-group.css +138 -0
- package/components/collapsible-group/collapsible-group.d.ts +20 -0
- package/components/collapsible-group/collapsible-group.js +73 -0
- package/components/editable-heading/editable-heading.js +14 -14
- package/components/expand/collapsible-group.css +23 -89
- package/components/expand/collapsible-group.d.ts +8 -20
- package/components/expand/collapsible-group.js +14 -73
- package/components/global/compose-refs.d.ts +2 -1
- package/components/global/compose-refs.js +24 -8
- 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 +29 -9
- package/components/global/intersection-observer-context.js +43 -24
- 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/schedule-with-cleanup.d.ts +12 -0
- package/components/global/schedule-with-cleanup.js +34 -0
- package/components/global/table-selection.js +1 -1
- package/components/input/input.d.ts +1 -1
- package/components/legacy-table/row.d.ts +1 -1
- package/components/popup/popup.d.ts +2 -0
- package/components/popup/popup.js +2 -2
- package/components/select/select-popup.d.ts +1 -1
- package/components/select/select.d.ts +1 -1
- package/components/table/default-item-renderer.d.ts +23 -10
- package/components/table/default-item-renderer.js +40 -19
- package/components/table/internal/column-animation.d.ts +16 -0
- package/components/table/internal/column-animation.js +45 -0
- package/components/table/internal/table-header.d.ts +4 -0
- package/components/table/internal/table-header.js +357 -0
- package/components/table/internal/virtual-items.d.ts +34 -0
- package/components/table/{table-virtualize.js → internal/virtual-items.js} +68 -33
- package/components/table/item-virtualization.d.ts +35 -0
- package/components/table/item-virtualization.js +28 -0
- package/components/table/table-const.d.ts +40 -6
- package/components/table/table-const.js +14 -5
- package/components/table/table-primitives.d.ts +9 -16
- package/components/table/table-primitives.js +6 -60
- package/components/table/table-props.d.ts +280 -0
- package/components/table/table-props.js +1 -0
- package/components/table/table.css +171 -66
- package/components/table/table.d.ts +205 -209
- package/components/table/table.js +298 -2
- package/components/util-stories.d.ts +26 -0
- package/components/util-stories.js +61 -0
- package/package.json +29 -30
- package/components/date-picker/use-intersection-observer.d.ts +0 -6
- package/components/date-picker/use-intersection-observer.js +0 -48
- package/components/global/composeRefs.d.ts +0 -6
- package/components/global/composeRefs.js +0 -7
- package/components/table/table-component.d.ts +0 -80
- package/components/table/table-component.js +0 -130
- package/components/table/table-row-focus.d.ts +0 -4
- package/components/table/table-row-focus.js +0 -42
- package/components/table/table-virtualize.d.ts +0 -32
|
@@ -1,90 +1,72 @@
|
|
|
1
1
|
@import '../global/variables.css';
|
|
2
2
|
|
|
3
3
|
.table {
|
|
4
|
+
--animated-column-fade-out-duration: 600ms;
|
|
5
|
+
|
|
4
6
|
width: 100%;
|
|
5
7
|
|
|
6
|
-
border-
|
|
8
|
+
border-spacing: 0;
|
|
9
|
+
|
|
7
10
|
border-style: none;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
|
-
.
|
|
11
|
-
|
|
13
|
+
.stickyHeader {
|
|
14
|
+
position: sticky;
|
|
15
|
+
z-index: var(--ring-overlay-z-index);
|
|
16
|
+
|
|
17
|
+
top: 0;
|
|
18
|
+
|
|
19
|
+
background-color: rgba(var(--ring-content-background-components), 0.85);
|
|
20
|
+
backdrop-filter: blur(8px);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@property --header-cell-shadow-color {
|
|
24
|
+
syntax: '<color>';
|
|
25
|
+
inherits: true;
|
|
26
|
+
initial-value: transparent;
|
|
12
27
|
}
|
|
13
28
|
|
|
14
29
|
.headerCell {
|
|
15
|
-
|
|
30
|
+
margin: 0;
|
|
16
31
|
|
|
17
32
|
padding: calc(var(--ring-unit) * 0.75) var(--ring-unit);
|
|
18
33
|
|
|
19
|
-
transition: background-color var(--ring-fast-ease),
|
|
20
|
-
|
|
21
|
-
text-align: left;
|
|
34
|
+
transition: background-color var(--ring-fast-ease), --header-cell-shadow-color var(--ring-fast-ease);
|
|
22
35
|
|
|
23
|
-
|
|
24
|
-
border-left: 1px solid transparent;
|
|
25
|
-
|
|
26
|
-
font-size: var(--ring-font-size-smaller);
|
|
27
|
-
font-weight: normal;
|
|
36
|
+
border-bottom: 1px solid var(--ring-line-color);
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
border-right: 1px solid transparent;
|
|
31
|
-
}
|
|
38
|
+
box-shadow: inset 1px 0 0 var(--header-cell-shadow-color), 1px 0 0 var(--header-cell-shadow-color);
|
|
32
39
|
|
|
33
40
|
&:hover {
|
|
34
|
-
|
|
35
|
-
background-color: var(--ring-grey-container-light-color);
|
|
36
|
-
|
|
37
|
-
&:last-child {
|
|
38
|
-
border-right-color: var(--ring-line-color);
|
|
39
|
-
}
|
|
41
|
+
--header-cell-shadow-color: var(--ring-line-color);
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
&:not(.animatedColumnInitial, .animatedColumnFadeOut) {
|
|
44
|
+
background-color: var(--ring-grey-container-light-color);
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
.headerCellInnerWrapper {
|
|
50
|
+
display: flex;
|
|
51
|
+
justify-content: start;
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.spacerCell {
|
|
55
|
-
margin: 0;
|
|
56
|
-
padding: 0;
|
|
53
|
+
color: var(--ring-secondary-color);
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
font-size: var(--ring-font-size-smaller);
|
|
56
|
+
font-weight: normal;
|
|
60
57
|
|
|
61
|
-
.
|
|
62
|
-
|
|
58
|
+
.sortAndHeader {
|
|
59
|
+
flex-grow: 1;
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
outline: 2px solid var(--ring-button-focus-border-color);
|
|
61
|
+
text-align: left;
|
|
62
|
+
white-space: nowrap;
|
|
67
63
|
}
|
|
68
|
-
}
|
|
69
64
|
|
|
70
|
-
.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
transition: background-color var(--ring-fast-ease);
|
|
74
|
-
|
|
75
|
-
&:not(.selectedRow):hover {
|
|
76
|
-
background-color: var(--ring-hover-background-color);
|
|
65
|
+
.rightButtons {
|
|
66
|
+
margin-left: 4px;
|
|
77
67
|
}
|
|
78
68
|
}
|
|
79
69
|
|
|
80
|
-
.selectedRow {
|
|
81
|
-
background-color: var(--ring-selected-background-color);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.cell {
|
|
85
|
-
padding: var(--ring-unit);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
70
|
.headerButton {
|
|
89
71
|
margin: unset;
|
|
90
72
|
padding: unset;
|
|
@@ -108,30 +90,153 @@
|
|
|
108
90
|
border-radius: 2px;
|
|
109
91
|
outline: 2px solid var(--ring-button-focus-border-color);
|
|
110
92
|
}
|
|
93
|
+
|
|
94
|
+
&:focus-within {
|
|
95
|
+
color: var(--ring-main-hover-color);
|
|
96
|
+
}
|
|
111
97
|
}
|
|
112
98
|
|
|
113
99
|
.deleteColumnButton {
|
|
114
|
-
float: right;
|
|
115
|
-
|
|
116
100
|
pointer-events: none;
|
|
117
101
|
|
|
118
102
|
color: transparent;
|
|
119
103
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
104
|
+
.theadColumnEditing &,
|
|
105
|
+
.headerCell:hover &,
|
|
106
|
+
&:focus {
|
|
107
|
+
pointer-events: unset;
|
|
108
|
+
}
|
|
124
109
|
|
|
125
|
-
|
|
110
|
+
/* stylelint-disable-next-line selector-max-specificity */
|
|
111
|
+
.theadColumnEditing &:not(:focus),
|
|
112
|
+
/* stylelint-disable-next-line selector-max-specificity */
|
|
113
|
+
.headerCell:hover &:not(:focus) {
|
|
114
|
+
color: unset;
|
|
115
|
+
}
|
|
126
116
|
|
|
127
117
|
/* stylelint-disable-next-line selector-max-specificity */
|
|
128
|
-
&:hover
|
|
118
|
+
.headerCell:hover &:hover:not(:focus),
|
|
119
|
+
&:focus {
|
|
129
120
|
color: var(--ring-main-hover-color);
|
|
130
121
|
}
|
|
131
122
|
}
|
|
132
123
|
|
|
133
|
-
.
|
|
134
|
-
|
|
124
|
+
.columnReorderHandle {
|
|
125
|
+
overflow: hidden;
|
|
126
|
+
|
|
127
|
+
width: 0;
|
|
128
|
+
|
|
129
|
+
cursor: grab;
|
|
130
|
+
|
|
131
|
+
transition: color var(--ring-fast-ease), width var(--ring-fast-ease), margin-right var(--ring-fast-ease);
|
|
132
|
+
|
|
133
|
+
border-radius: var(--ring-border-radius);
|
|
134
|
+
touch-action: none;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.theadColumnEditing .columnReorderHandle,
|
|
138
|
+
.headerCell:hover .columnReorderHandle,
|
|
139
|
+
.columnReorderHandle:focus-visible {
|
|
140
|
+
width: 12px;
|
|
141
|
+
margin-right: 4px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.columnReorderHandleMirror {
|
|
145
|
+
display: inline-block;
|
|
146
|
+
|
|
147
|
+
width: 12px;
|
|
148
|
+
margin-left: 4px;
|
|
149
|
+
|
|
150
|
+
transition: width var(--ring-fast-ease), margin-left var(--ring-fast-ease);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.theadColumnEditing .columnReorderHandleMirror,
|
|
154
|
+
.headerCell:hover .columnReorderHandleMirror,
|
|
155
|
+
.columnReorderHandle:focus-visible ~ .columnReorderHandleMirror {
|
|
156
|
+
width: 0;
|
|
157
|
+
margin-left: 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.editColumnsButton {
|
|
161
|
+
& svg {
|
|
162
|
+
transition: transform var(--ring-fast-ease);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.theadColumnEditing & svg {
|
|
166
|
+
transform: rotate(90deg);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.columnDragFrame {
|
|
171
|
+
position: fixed;
|
|
172
|
+
|
|
173
|
+
z-index: var(--ring-overlay-z-index);
|
|
174
|
+
|
|
175
|
+
pointer-events: none;
|
|
176
|
+
|
|
177
|
+
border: 2px solid var(--ring-border-accent-color);
|
|
178
|
+
border-radius: calc(2 * var(--ring-border-radius));
|
|
179
|
+
|
|
180
|
+
background-color: rgba(var(--ring-border-accent-components), 0.06);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.columnInsertionIndicator {
|
|
184
|
+
position: fixed;
|
|
185
|
+
|
|
186
|
+
z-index: var(--ring-overlay-z-index);
|
|
135
187
|
|
|
136
|
-
|
|
188
|
+
width: 2px;
|
|
189
|
+
|
|
190
|
+
background-color: var(--ring-main-color);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.animatedColumnInitial {
|
|
194
|
+
transition: none;
|
|
195
|
+
|
|
196
|
+
background-color: rgba(var(--ring-border-accent-components), 0.12);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.animatedColumnFadeOut {
|
|
200
|
+
transition: background-color var(--animated-column-fade-out-duration) ease-out;
|
|
201
|
+
|
|
202
|
+
background-color: transparent;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.spacerRow {
|
|
206
|
+
margin: 0;
|
|
207
|
+
padding: 0;
|
|
208
|
+
|
|
209
|
+
border: 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.spacerCell {
|
|
213
|
+
margin: 0;
|
|
214
|
+
padding: 0;
|
|
215
|
+
|
|
216
|
+
border: 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.row:focus {
|
|
220
|
+
border-radius: var(--ring-border-radius);
|
|
221
|
+
outline: 2px solid var(--ring-button-focus-border-color);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.clickableRow {
|
|
225
|
+
cursor: pointer;
|
|
226
|
+
|
|
227
|
+
transition: background-color var(--ring-fast-ease);
|
|
228
|
+
|
|
229
|
+
&:not(.selectedRow):hover {
|
|
230
|
+
background-color: var(--ring-hover-background-color);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.selectedRow {
|
|
235
|
+
background-color: var(--ring-selected-background-color);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.cell {
|
|
239
|
+
padding: var(--ring-unit);
|
|
240
|
+
|
|
241
|
+
border-bottom: 1px solid var(--ring-line-color);
|
|
137
242
|
}
|
|
@@ -1,211 +1,207 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
export default Table;
|
|
4
|
-
export interface TableProps<T> {
|
|
5
|
-
/**
|
|
6
|
-
* The data items to render. `null` and `undefined` as an item is not supported.
|
|
7
|
-
* Referentially same items are not supported either.
|
|
8
|
-
*/
|
|
9
|
-
data: T[];
|
|
10
|
-
/**
|
|
11
|
-
* Columns specification.
|
|
12
|
-
*/
|
|
13
|
-
columns: Column<T>[];
|
|
14
|
-
/**
|
|
15
|
-
* Used to render a row key, e.g. `<tr key={getKey(item, index)}>`.
|
|
16
|
-
*/
|
|
17
|
-
getKey: (item: T, index: number, items: T[]) => React.Key;
|
|
18
|
-
/**
|
|
19
|
-
* If true, the table header will not be rendered.
|
|
20
|
-
*/
|
|
21
|
-
noHeader?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Called when the client moves a row by dragging it.
|
|
24
|
-
*/
|
|
25
|
-
onItemMove?: (item: T, fromIndex: number, toIndex: number, items: T[]) => void;
|
|
26
|
-
/**
|
|
27
|
-
* Called when the client clicks on SortButton in a column header.
|
|
28
|
-
*/
|
|
29
|
-
onSort?: (columnIndex: number, newOrder: SortOrder, columns: Column<T>[]) => void;
|
|
30
|
-
/**
|
|
31
|
-
* Called when the client clicks on a column delete button in the header.
|
|
32
|
-
*/
|
|
33
|
-
onColumnDelete?: (columnIndex: number, columns: Column<T>[]) => void;
|
|
34
|
-
/**
|
|
35
|
-
* Called when the client moves a column.
|
|
36
|
-
*/
|
|
37
|
-
onColumnMove?: (fromIndex: number, toIndex: number, columns: Column<T>[]) => void;
|
|
38
|
-
/**
|
|
39
|
-
* Implement to specify props like `clickable`, handlers like `onClick`,
|
|
40
|
-
* a custom `className`, a `ref` etc., or to provide a custom renderer.
|
|
41
|
-
*
|
|
42
|
-
* ```tsx
|
|
43
|
-
* <Table
|
|
44
|
-
* renderItem={(item, index) => (
|
|
45
|
-
* <DefaultItemRenderer
|
|
46
|
-
* index={index}
|
|
47
|
-
* className='my-row'
|
|
48
|
-
* clickable
|
|
49
|
-
* onClick={() => setSelection(selection.focus(item))}
|
|
50
|
-
* />
|
|
51
|
-
* )}
|
|
52
|
-
* />
|
|
53
|
-
* ```
|
|
54
|
-
*
|
|
55
|
-
* In your custom implementation, use `TableRow` and `TableCell` base components to apply the
|
|
56
|
-
* standard classnames, but beware that `tdClassName` won't be applied.
|
|
57
|
-
*
|
|
58
|
-
* You can also use `DefaultItemRenderer` in combination with your custom rows.
|
|
59
|
-
*/
|
|
60
|
-
renderItem?: (item: T, index: number, items: T[]) => ReactNode;
|
|
61
|
-
/**
|
|
62
|
-
* Only renders rows near the viewport.
|
|
63
|
-
*
|
|
64
|
-
* Rows may transition between two states:
|
|
65
|
-
* - materialized: rendered as actual table rows. This happens when the corresponding
|
|
66
|
-
* spacer approaches the viewport, as specified by `lookaheadPx`.
|
|
67
|
-
* - virtualized: replaced with spacer rows of the same height. This happens when the row
|
|
68
|
-
* moves sufficiently far from the viewport, as specified by `retentionMarginPx`.
|
|
69
|
-
*/
|
|
70
|
-
virtualizeRows?: boolean;
|
|
71
|
-
/**
|
|
72
|
-
* Used with `virtualizeRows` as a source of scroll events, as a target for ResizeObserver,
|
|
73
|
-
* and as the root for IntersectionObserver. Required when the scrollable container is not
|
|
74
|
-
* the whole document.
|
|
75
|
-
*
|
|
76
|
-
* If not set:
|
|
77
|
-
* - scroll listener is attached to `window`
|
|
78
|
-
* - ResizeObserver observes `document.body`
|
|
79
|
-
* - IntersectionObserver has no root (i.e. the viewport is used)
|
|
80
|
-
*/
|
|
81
|
-
scrollerRef?: RefObject<HTMLElement | null>;
|
|
82
|
-
/**
|
|
83
|
-
* Used with `virtualizeRows` to estimate the height of items that have not been rendered yet.
|
|
84
|
-
* The function should be fast and side-effect free. Do not measure the DOM here.
|
|
85
|
-
* Once a row is rendered, its actual height will be measured and used instead of this estimate.
|
|
86
|
-
*
|
|
87
|
-
* Note the effects of imprecise estimates:
|
|
88
|
-
* - When the height is underestimated, the table may materialize more rows than specified by `lookaheadPx`.
|
|
89
|
-
* If the resulting rows extend beyond `retentionMarginPx`, they will be virtualized again.
|
|
90
|
-
* If this causes relayout flickering, increase `retentionMarginPx`.
|
|
91
|
-
* - When the height is overestimated, the table may materialize fewer rows than specified by `lookaheadPx`,
|
|
92
|
-
* which may leave a spacer partially visible. To avoid this, increase `lookaheadPx` (and
|
|
93
|
-
* `retentionMarginPx` accordingly, since it should be greater than `lookaheadPx`).
|
|
94
|
-
*
|
|
95
|
-
* Default: 37px = 16px padding + 20px line height + 1px border.
|
|
96
|
-
*/
|
|
97
|
-
estimateHeight?: (item: T, index: number, items: T[]) => number;
|
|
98
|
-
/**
|
|
99
|
-
* When using `virtualizeRows`, the number of pixels above and below the viewport
|
|
100
|
-
* to materialize in advance.
|
|
101
|
-
*
|
|
102
|
-
* Increase this value if blank space becomes visible during fast scrolling.
|
|
103
|
-
*
|
|
104
|
-
* Default: 400px.
|
|
105
|
-
*/
|
|
106
|
-
lookaheadPx?: number;
|
|
107
|
-
/**
|
|
108
|
-
* Used with `virtualizeRows`. Additional margin around the viewport before
|
|
109
|
-
* materialized rows become eligible for virtualization.
|
|
110
|
-
*
|
|
111
|
-
* Increasing this value reduces row churn when heights are underestimated.
|
|
112
|
-
* In that case, the table may materialize more rows than needed and then immediately
|
|
113
|
-
* virtualize them again. A larger margin keeps such rows rendered for longer,
|
|
114
|
-
* at the cost of rendering more rows overall.
|
|
115
|
-
*
|
|
116
|
-
* This value should be greater than `lookaheadPx`.
|
|
117
|
-
* Increase it if you notice table relayouts during initial render or scrolling.
|
|
118
|
-
*
|
|
119
|
-
* Default: 450px.
|
|
120
|
-
*/
|
|
121
|
-
retentionMarginPx?: number;
|
|
122
|
-
/**
|
|
123
|
-
* When using `virtualizeRows`, ignore scroll and resize position changes
|
|
124
|
-
* smaller than this value.
|
|
125
|
-
*
|
|
126
|
-
* Measurement inaccuracies and rounding artifacts may slightly change the
|
|
127
|
-
* table layout during materialization and virtualization. With scroll
|
|
128
|
-
* anchoring enabled (the default browser behavior), the browser may then
|
|
129
|
-
* adjust the scroll position, triggering additional scroll or resize events.
|
|
130
|
-
* Small deltas are ignored to prevent such feedback loops from causing
|
|
131
|
-
* oscillations at virtualization boundaries.
|
|
132
|
-
*
|
|
133
|
-
* Increase if you expect high inaccuracy in height measurements, or if you
|
|
134
|
-
* notice oscillations at virtualization boundaries.
|
|
135
|
-
*
|
|
136
|
-
* Default: 50px.
|
|
137
|
-
*/
|
|
138
|
-
minScrollAndResizeDeltaPx?: number;
|
|
139
|
-
/**
|
|
140
|
-
* Whether to show a small gear button at the top right corner.
|
|
141
|
-
*/
|
|
142
|
-
columnEditButton?: 'everywhere' | 'mobileOnly';
|
|
143
|
-
/**
|
|
144
|
-
* Applied to the `<thead>` element.
|
|
145
|
-
*/
|
|
146
|
-
theadClassName?: string;
|
|
147
|
-
/**
|
|
148
|
-
* Applied to the only `<tr>` element within the `<thead>`.
|
|
149
|
-
*/
|
|
150
|
-
theadTrClassName?: string;
|
|
151
|
-
/**
|
|
152
|
-
* Applied to the `<tbody>` element.
|
|
153
|
-
*/
|
|
154
|
-
tbodyClassName?: string;
|
|
155
|
-
}
|
|
156
|
-
export type SortOrder = 'none' | 'ascending' | 'descending';
|
|
1
|
+
import React, { type ComponentPropsWithRef } from 'react';
|
|
2
|
+
import type { TableProps } from './table-props';
|
|
157
3
|
/**
|
|
158
|
-
*
|
|
4
|
+
* Table component replacing the tables in the `legacy-table` folder.
|
|
5
|
+
*
|
|
6
|
+
* This documentation provides an overview of the most common usage patterns.
|
|
7
|
+
* See individual props and exported components for detailed behavior.
|
|
8
|
+
*
|
|
9
|
+
* ## Minimal usage
|
|
10
|
+
*
|
|
11
|
+
* You need the following props:
|
|
12
|
+
* - `data`
|
|
13
|
+
* - `getKey`
|
|
14
|
+
* - `columns`
|
|
15
|
+
* - `key`
|
|
16
|
+
* - `name` (optional but needed in most cases)
|
|
17
|
+
* - `renderCell` (optional but needed in most cases)
|
|
18
|
+
*
|
|
19
|
+
* ## Item rendering
|
|
20
|
+
*
|
|
21
|
+
* If `renderItem` is not specified, each item is rendered using
|
|
22
|
+
* `DefaultItemRenderer` (from `table/default-item-renderer`)
|
|
23
|
+
* as if the following code were used:
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <Table
|
|
27
|
+
* renderItem={(_item, index, _items) => (
|
|
28
|
+
* <DefaultItemRenderer index={index} />
|
|
29
|
+
* )}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* `DefaultItemRenderer` renders a table row using the column definitions
|
|
34
|
+
* (`Column.renderCell`) and provides built-in support for features such as
|
|
35
|
+
* selection, keyboard navigation, and virtualization. It also accepts all
|
|
36
|
+
* standard `tr` attributes, including `ref`.
|
|
37
|
+
*
|
|
38
|
+
* Use `renderItem` to configure `DefaultItemRenderer` for each item:
|
|
39
|
+
*
|
|
40
|
+
* ```tsx
|
|
41
|
+
* <Table
|
|
42
|
+
* renderItem={(item, index, items) => (
|
|
43
|
+
* <DefaultItemRenderer
|
|
44
|
+
* index={index}
|
|
45
|
+
* keyboardFocusable
|
|
46
|
+
* className='my-item'
|
|
47
|
+
* onClick={e => handleClick(e, item, items)}
|
|
48
|
+
* />
|
|
49
|
+
* )}
|
|
50
|
+
* />
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* If you need complete control over rendering, `renderItem` can instead
|
|
54
|
+
* return your own table rows. See "Custom item rendering" below.
|
|
55
|
+
*
|
|
56
|
+
* ## Selection
|
|
57
|
+
*
|
|
58
|
+
* Selection is typically implemented using the following props
|
|
59
|
+
* of the `DefaultItemRenderer`:
|
|
60
|
+
*
|
|
61
|
+
* - `clickable`
|
|
62
|
+
* - `selected`
|
|
63
|
+
* - `onClick` or `onPointerUp`, etc.
|
|
64
|
+
*
|
|
65
|
+
* The following utilities (from `global`) may come in handy:
|
|
66
|
+
*
|
|
67
|
+
* - `TableSelection` class to manage selection state
|
|
68
|
+
* - An alternative approach is to keep a `selected` field on each item
|
|
69
|
+
* - `isWithinInteractiveElement()` to check if a click was made on a control
|
|
70
|
+
* or on "empty space"
|
|
71
|
+
*
|
|
72
|
+
* ```tsx
|
|
73
|
+
* <Table
|
|
74
|
+
* renderItem={(item, index) => (
|
|
75
|
+
* <DefaultItemRenderer
|
|
76
|
+
* index={index}
|
|
77
|
+
* clickable
|
|
78
|
+
* selected={selection.isSelected(item)}
|
|
79
|
+
* onClick={e => {
|
|
80
|
+
* if (!isWithinInteractiveElement(e)) {
|
|
81
|
+
* setSelection(selection.toggleSelection(item));
|
|
82
|
+
* }
|
|
83
|
+
* }}
|
|
84
|
+
* />
|
|
85
|
+
* )}
|
|
86
|
+
* />
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* Note that for accessibility reasons, you should have a cell with a checkbox
|
|
90
|
+
* to display and toggle item selection.
|
|
91
|
+
*
|
|
92
|
+
* ## Rows focus
|
|
93
|
+
*
|
|
94
|
+
* The table implements the ["roving tabindex"](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Guides/Keyboard-navigable_JavaScript_widgets#technique_1_roving_tabindex)
|
|
95
|
+
* technique to focus rows with the up/down arrow keys.
|
|
96
|
+
* Rows can also be focused on click or other pointer events.
|
|
97
|
+
* To support it, use the following props of the `DefaultItemRenderer`:
|
|
98
|
+
*
|
|
99
|
+
* - `keyboardFocusable`
|
|
100
|
+
* - `clickable`, if you want to react to hover
|
|
101
|
+
* - `onClick`, if you want to focus on click
|
|
102
|
+
*
|
|
103
|
+
* Useful utils:
|
|
104
|
+
* - `focusWithTemporaryTabIndex()` from `global` to focus a row temporarily
|
|
105
|
+
* patching its `tabindex`.
|
|
106
|
+
*
|
|
107
|
+
* ```tsx
|
|
108
|
+
* <Table
|
|
109
|
+
* renderItem={(item, index) => (
|
|
110
|
+
* <DefaultItemRenderer
|
|
111
|
+
* index={index}
|
|
112
|
+
* clickable
|
|
113
|
+
* keyboardFocusable
|
|
114
|
+
* onClick={e => {
|
|
115
|
+
* if (!isWithinInteractiveElement(e)) {
|
|
116
|
+
* focusWithTemporaryTabIndex(e.currentTarget);
|
|
117
|
+
* }
|
|
118
|
+
* }}
|
|
119
|
+
* />
|
|
120
|
+
* )}
|
|
121
|
+
* />
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* Note that the table does not implement standard accessibility patterns such
|
|
125
|
+
* as `grid` or `treegrid`, so row focus is not announced by screen readers.
|
|
126
|
+
* Make sure all essential actions remain available without row focus, for
|
|
127
|
+
* example via standard Tab navigation.
|
|
128
|
+
*
|
|
129
|
+
* ## Sorting
|
|
130
|
+
*
|
|
131
|
+
* You need the following to support sorting:
|
|
132
|
+
*
|
|
133
|
+
* - Set `Column.sortOrder` to `'none'`, `'ascending'` or `'descending'`
|
|
134
|
+
* to render the sort button, `aria-sort`, and indicate the current
|
|
135
|
+
* sort order.
|
|
136
|
+
* - Handle `TableProps.onSort` callback in the client code.
|
|
137
|
+
*
|
|
138
|
+
* ## Deleting columns
|
|
139
|
+
*
|
|
140
|
+
* You need the following to support deleting columns:
|
|
141
|
+
*
|
|
142
|
+
* - Set `Column.deletable` to `true`. This will render a delete button in the
|
|
143
|
+
* column header.
|
|
144
|
+
* - Make sure the `column` has a proper `name` or `key` prop, which will be
|
|
145
|
+
* automatically included in the aria-label of the column delete button.
|
|
146
|
+
* - Handle `TableProps.onColumnDelete` callback in the client code. It is
|
|
147
|
+
* expected to update `columns` by removing the corresponding column.
|
|
148
|
+
*
|
|
149
|
+
* ## Moving columns
|
|
150
|
+
*
|
|
151
|
+
* - Set `Column.canReorder` to `true` or to predicate specifying possible
|
|
152
|
+
* insertion targets.
|
|
153
|
+
* This will render a reorder button in the column header.
|
|
154
|
+
* - Make sure the `column` has a proper `name` or `key` prop, which will be
|
|
155
|
+
* automatically included in the aria-label of the column reorder button.
|
|
156
|
+
* - Handle `TableProps.onColumnReorder` callback in the client code. It is
|
|
157
|
+
* expected to update `columns` by moving the corresponding column to the
|
|
158
|
+
* new position.
|
|
159
|
+
*
|
|
160
|
+
* ## Row virtualization
|
|
161
|
+
*
|
|
162
|
+
* To render only rows near the viewport while replacing off-screen rows with
|
|
163
|
+
* spacers, use:
|
|
164
|
+
*
|
|
165
|
+
* - `virtualizeRows` prop set to `true`
|
|
166
|
+
* - `scrollerRef` — required when the scrollable container is not the whole
|
|
167
|
+
* document
|
|
168
|
+
* - `estimateHeight` — recommended when rows are expected to be taller than
|
|
169
|
+
* the default height (e.g. multiline or custom content)
|
|
170
|
+
* - Fine-tuning props: `lookaheadPx`, `retentionMarginPx`,
|
|
171
|
+
* `minScrollAndResizeDeltaPx`
|
|
172
|
+
*
|
|
173
|
+
* ## Custom item rendering
|
|
174
|
+
*
|
|
175
|
+
* Use the `renderItem` prop to render an item in a completely custom way.
|
|
176
|
+
* The prop is expected to return one or more table rows for the item.
|
|
177
|
+
* Use `TableRow` and `TableCell` from `table/table-primitives` to apply
|
|
178
|
+
* the default row and cell styles.
|
|
179
|
+
*
|
|
180
|
+
* ### Focus
|
|
181
|
+
*
|
|
182
|
+
* Just like `DefaultItemRenderer`, `TableRow` accepts the
|
|
183
|
+
* `keyboardFocusable` prop.
|
|
184
|
+
*
|
|
185
|
+
* Focusable rows rendered by either component form a single keyboard
|
|
186
|
+
* navigation sequence.
|
|
187
|
+
*
|
|
188
|
+
* ### Virtualization
|
|
189
|
+
*
|
|
190
|
+
* If `Table.virtualizeRows` is set to `true`, you need to handle visibility
|
|
191
|
+
* for your custom-rendered component yourself with the
|
|
192
|
+
* `useItemVirtualization()` hook (from `table/item-virtualization`). The hook
|
|
193
|
+
* allows observing the intersection of one or multiple elements rendered for
|
|
194
|
+
* the item, and, based on their intersection status, reporting the item as
|
|
195
|
+
* eligible for virtualization.
|
|
196
|
+
*
|
|
197
|
+
* If you use `DefaultItemRenderer` as part of your custom row renderer,
|
|
198
|
+
* set the `noItemVirtualization` prop to `true`, otherwise it will also try
|
|
199
|
+
* to control the virtualization, possibly reporting incorrect item height.
|
|
200
|
+
*
|
|
201
|
+
* ### Column reorder animation
|
|
202
|
+
*
|
|
203
|
+
* Default-rendered rows highlight the column that was just reordered. To apply
|
|
204
|
+
* the same animation to your custom-rendered rows, use `ColumnAnimationContext`
|
|
205
|
+
* (from `table/table-const`) to get information about the currently animated column.
|
|
159
206
|
*/
|
|
160
|
-
export
|
|
161
|
-
/**
|
|
162
|
-
* Used to render a row key, e.g. `<thead><tr><td key={getKey(item, index, items)}...</td></tr></thead>`.
|
|
163
|
-
*/
|
|
164
|
-
key: React.Key;
|
|
165
|
-
/**
|
|
166
|
-
* Used in aria-labels of controls which do not contain text,
|
|
167
|
-
* e.g. `DeleteColumnButton`. If not set, the `String(key)` is used.
|
|
168
|
-
*/
|
|
169
|
-
name?: string;
|
|
170
|
-
/**
|
|
171
|
-
* Default: name ?? String(key)
|
|
172
|
-
*/
|
|
173
|
-
renderHeader?: () => ReactNode;
|
|
174
|
-
/**
|
|
175
|
-
* Renders a single cell value for a column.
|
|
176
|
-
* Default:
|
|
177
|
-
* - If item is an Array, renders `String(item[columnIndex])`
|
|
178
|
-
* - If item is an Object, renders `Object.values(item)[columnIndex]`
|
|
179
|
-
* - Otherwise:
|
|
180
|
-
* - The first column renders `String(item)`
|
|
181
|
-
* - Other columns render empty value
|
|
182
|
-
*/
|
|
183
|
-
renderCell?: (item: T, index: number, items: T[]) => ReactNode;
|
|
184
|
-
/**
|
|
185
|
-
* If the column gets an indent when `TableProps.getItemLevel()` returns
|
|
186
|
-
* a positive number.
|
|
187
|
-
*/
|
|
188
|
-
indent?: boolean;
|
|
189
|
-
/**
|
|
190
|
-
* The current sort order displayed by `SortButton` and indicated
|
|
191
|
-
* by `aria-sort` in `th`.
|
|
192
|
-
*
|
|
193
|
-
* - `undefined` or not set means that the column is not sortable,
|
|
194
|
-
* and it shouldn't have a `SortButton`.
|
|
195
|
-
* - `none` means that the column is sortable, but currently not sorted.
|
|
196
|
-
* It should include a `SortButton`.
|
|
197
|
-
* - `ascending` and `descending` mean that the column is sorted
|
|
198
|
-
* in the corresponding order, and it should include a `SortButton`.
|
|
199
|
-
*/
|
|
200
|
-
sortOrder?: SortOrder;
|
|
201
|
-
/**
|
|
202
|
-
* The classname to apply to the `th` element inside `table / thead`.
|
|
203
|
-
*/
|
|
204
|
-
thClassName?: string;
|
|
205
|
-
/**
|
|
206
|
-
* The classname to apply to the `td` element inside `table / tbody`.
|
|
207
|
-
* If a custom `TableProps.renderItem` is provided, this prop is not used,
|
|
208
|
-
* unless the custom renderer falls back to `DefaultItemRenderer`.
|
|
209
|
-
*/
|
|
210
|
-
tdClassName?: (item: T, index: number, items: T[]) => string | undefined;
|
|
211
|
-
}
|
|
207
|
+
export default function Table<T>(props: TableProps<T> & ComponentPropsWithRef<'table'>): React.JSX.Element;
|