@instructure/ui-table 11.0.1-snapshot-0 → 11.0.1-snapshot-2
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/CHANGELOG.md +1 -1
- package/es/Table/TableContext.js +1 -1
- package/lib/Table/TableContext.js +1 -1
- package/package.json +14 -14
- package/src/Table/README.md +960 -2104
- package/src/Table/TableContext.ts +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Table/TableContext.d.ts +1 -1
package/src/Table/README.md
CHANGED
|
@@ -11,414 +11,212 @@ In stacked layout, column header is rendered in each cell, but not in row header
|
|
|
11
11
|
> exceed the bounds of the table cell, use `fixed` or `stacked`, together with the [Text](#Text) component:
|
|
12
12
|
> `<Text wrap="break-word">[long string]</Text>`.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
renderOptions() {
|
|
28
|
-
const { layout, hover } = this.state
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<Flex alignItems="start">
|
|
32
|
-
<Flex.Item margin="small">
|
|
33
|
-
<RadioInputGroup
|
|
34
|
-
name="layout"
|
|
35
|
-
description="layout"
|
|
36
|
-
value={layout}
|
|
37
|
-
onChange={(e, value) => this.handleChange('layout', value)}
|
|
38
|
-
>
|
|
39
|
-
<RadioInput label="auto" value="auto" />
|
|
40
|
-
<RadioInput label="fixed" value="fixed" />
|
|
41
|
-
<RadioInput label="stacked" value="stacked" />
|
|
42
|
-
</RadioInputGroup>
|
|
43
|
-
</Flex.Item>
|
|
44
|
-
<Flex.Item margin="small">
|
|
45
|
-
<Checkbox
|
|
46
|
-
label="hover"
|
|
47
|
-
checked={hover}
|
|
48
|
-
onChange={(e, value) => this.handleChange('hover', !hover)}
|
|
49
|
-
/>
|
|
50
|
-
</Flex.Item>
|
|
51
|
-
</Flex>
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
render() {
|
|
56
|
-
const { layout, hover } = this.state
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<div>
|
|
60
|
-
{this.renderOptions()}
|
|
61
|
-
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
62
|
-
<Table.Head>
|
|
63
|
-
<Table.Row>
|
|
64
|
-
<Table.ColHeader id="Rank">Rank</Table.ColHeader>
|
|
65
|
-
<Table.ColHeader id="Title">Title</Table.ColHeader>
|
|
66
|
-
<Table.ColHeader id="Year">Year</Table.ColHeader>
|
|
67
|
-
<Table.ColHeader id="Rating">Rating</Table.ColHeader>
|
|
68
|
-
</Table.Row>
|
|
69
|
-
</Table.Head>
|
|
70
|
-
<Table.Body>
|
|
71
|
-
<Table.Row>
|
|
72
|
-
<Table.RowHeader>1</Table.RowHeader>
|
|
73
|
-
<Table.Cell>The Shawshank Redemption</Table.Cell>
|
|
74
|
-
<Table.Cell>1994</Table.Cell>
|
|
75
|
-
<Table.Cell>9.3</Table.Cell>
|
|
76
|
-
</Table.Row>
|
|
77
|
-
<Table.Row>
|
|
78
|
-
<Table.RowHeader>2</Table.RowHeader>
|
|
79
|
-
<Table.Cell>The Godfather</Table.Cell>
|
|
80
|
-
<Table.Cell>1972</Table.Cell>
|
|
81
|
-
<Table.Cell>9.2</Table.Cell>
|
|
82
|
-
</Table.Row>
|
|
83
|
-
<Table.Row>
|
|
84
|
-
<Table.RowHeader>3</Table.RowHeader>
|
|
85
|
-
<Table.Cell>The Godfather: Part II</Table.Cell>
|
|
86
|
-
<Table.Cell>1974</Table.Cell>
|
|
87
|
-
<Table.Cell>9.0</Table.Cell>
|
|
88
|
-
</Table.Row>
|
|
89
|
-
</Table.Body>
|
|
90
|
-
</Table>
|
|
91
|
-
</div>
|
|
92
|
-
)
|
|
14
|
+
```js
|
|
15
|
+
---
|
|
16
|
+
type: example
|
|
17
|
+
---
|
|
18
|
+
const Example = () => {
|
|
19
|
+
const [layout, setLayout] = useState('auto')
|
|
20
|
+
const [hover, setHover] = useState(false)
|
|
21
|
+
|
|
22
|
+
const handleChange = (field, value) => {
|
|
23
|
+
if (field === 'layout') {
|
|
24
|
+
setLayout(value)
|
|
25
|
+
} else if (field === 'hover') {
|
|
26
|
+
setHover(value)
|
|
93
27
|
}
|
|
94
28
|
}
|
|
95
29
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
onChange={(e, value) => handleChange('layout', value)}
|
|
120
|
-
>
|
|
121
|
-
<RadioInput label="auto" value="auto" />
|
|
122
|
-
<RadioInput label="fixed" value="fixed" />
|
|
123
|
-
<RadioInput label="stacked" value="stacked" />
|
|
124
|
-
</RadioInputGroup>
|
|
125
|
-
</Flex.Item>
|
|
126
|
-
<Flex.Item margin="small">
|
|
127
|
-
<Checkbox
|
|
128
|
-
label="hover"
|
|
129
|
-
checked={hover}
|
|
130
|
-
onChange={(e, value) => handleChange('hover', !hover)}
|
|
131
|
-
/>
|
|
132
|
-
</Flex.Item>
|
|
133
|
-
</Flex>
|
|
134
|
-
)
|
|
30
|
+
const renderOptions = () => (
|
|
31
|
+
<Flex alignItems="start">
|
|
32
|
+
<Flex.Item margin="small">
|
|
33
|
+
<RadioInputGroup
|
|
34
|
+
name="layout"
|
|
35
|
+
description="layout"
|
|
36
|
+
value={layout}
|
|
37
|
+
onChange={(e, value) => handleChange('layout', value)}
|
|
38
|
+
>
|
|
39
|
+
<RadioInput label="auto" value="auto" />
|
|
40
|
+
<RadioInput label="fixed" value="fixed" />
|
|
41
|
+
<RadioInput label="stacked" value="stacked" />
|
|
42
|
+
</RadioInputGroup>
|
|
43
|
+
</Flex.Item>
|
|
44
|
+
<Flex.Item margin="small">
|
|
45
|
+
<Checkbox
|
|
46
|
+
label="hover"
|
|
47
|
+
checked={hover}
|
|
48
|
+
onChange={(e, value) => handleChange('hover', !hover)}
|
|
49
|
+
/>
|
|
50
|
+
</Flex.Item>
|
|
51
|
+
</Flex>
|
|
52
|
+
)
|
|
135
53
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
54
|
+
return (
|
|
55
|
+
<div>
|
|
56
|
+
{renderOptions()}
|
|
57
|
+
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
58
|
+
<Table.Head>
|
|
59
|
+
<Table.Row>
|
|
60
|
+
<Table.ColHeader id="Rank">Rank</Table.ColHeader>
|
|
61
|
+
<Table.ColHeader id="Title">Title</Table.ColHeader>
|
|
62
|
+
<Table.ColHeader id="Year">Year</Table.ColHeader>
|
|
63
|
+
<Table.ColHeader id="Rating">Rating</Table.ColHeader>
|
|
64
|
+
</Table.Row>
|
|
65
|
+
</Table.Head>
|
|
66
|
+
<Table.Body>
|
|
67
|
+
<Table.Row>
|
|
68
|
+
<Table.RowHeader>1</Table.RowHeader>
|
|
69
|
+
<Table.Cell>The Shawshank Redemption</Table.Cell>
|
|
70
|
+
<Table.Cell>1994</Table.Cell>
|
|
71
|
+
<Table.Cell>9.3</Table.Cell>
|
|
72
|
+
</Table.Row>
|
|
73
|
+
<Table.Row>
|
|
74
|
+
<Table.RowHeader>2</Table.RowHeader>
|
|
75
|
+
<Table.Cell>The Godfather</Table.Cell>
|
|
76
|
+
<Table.Cell>1972</Table.Cell>
|
|
77
|
+
<Table.Cell>9.2</Table.Cell>
|
|
78
|
+
</Table.Row>
|
|
79
|
+
<Table.Row>
|
|
80
|
+
<Table.RowHeader>3</Table.RowHeader>
|
|
81
|
+
<Table.Cell>The Godfather: Part II</Table.Cell>
|
|
82
|
+
<Table.Cell>1974</Table.Cell>
|
|
83
|
+
<Table.Cell>9.0</Table.Cell>
|
|
84
|
+
</Table.Row>
|
|
85
|
+
</Table.Body>
|
|
86
|
+
</Table>
|
|
87
|
+
</div>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
172
90
|
|
|
173
|
-
|
|
174
|
-
|
|
91
|
+
render(<Example />)
|
|
92
|
+
```
|
|
175
93
|
|
|
176
94
|
### Column width and alignment
|
|
177
95
|
|
|
178
96
|
Each column (`ColHeader`) can have a custom width, and each cell (`ColHeader`, `RowHeader` or `Cell`)
|
|
179
97
|
can be aligned differently.
|
|
180
98
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
key={id}
|
|
220
|
-
textAlign={layout === 'stacked' ? 'start' : textAlign}
|
|
221
|
-
>
|
|
222
|
-
{renderCell ? renderCell(row[id], layout) : row[id]}
|
|
223
|
-
</Table.Cell>
|
|
224
|
-
))}
|
|
225
|
-
</Table.Row>
|
|
226
|
-
))}
|
|
227
|
-
</Table.Body>
|
|
228
|
-
</Table>
|
|
229
|
-
</div>
|
|
230
|
-
)}
|
|
231
|
-
</Responsive>
|
|
232
|
-
)
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const renderSummary = (summary, layout) =>
|
|
237
|
-
layout === 'stacked' ? (
|
|
238
|
-
summary
|
|
239
|
-
) : (
|
|
240
|
-
<TruncateText truncate="word" ellipsis="...">
|
|
241
|
-
{summary}
|
|
242
|
-
</TruncateText>
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
render(
|
|
246
|
-
<Example
|
|
247
|
-
headers={[
|
|
248
|
-
{
|
|
249
|
-
id: 'Title',
|
|
250
|
-
text: 'Title',
|
|
251
|
-
width: '25%',
|
|
252
|
-
textAlign: 'start'
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
id: 'Year',
|
|
256
|
-
text: 'Year',
|
|
257
|
-
width: '15%',
|
|
258
|
-
textAlign: 'start'
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
id: 'Summary',
|
|
262
|
-
text: 'Summary',
|
|
263
|
-
width: '40%',
|
|
264
|
-
renderCell: renderSummary,
|
|
265
|
-
textAlign: 'start'
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
id: 'BoxOffice',
|
|
269
|
-
text: 'Box Office',
|
|
270
|
-
width: '20%',
|
|
271
|
-
textAlign: 'end'
|
|
272
|
-
}
|
|
273
|
-
]}
|
|
274
|
-
rows={[
|
|
275
|
-
{
|
|
276
|
-
id: '1',
|
|
277
|
-
Title: 'The Shawshank Redemption',
|
|
278
|
-
Year: 1994,
|
|
279
|
-
Summary:
|
|
280
|
-
'Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.',
|
|
281
|
-
BoxOffice: '$28,341,469'
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
id: '2',
|
|
285
|
-
Title: 'The Godfather',
|
|
286
|
-
Year: 1972,
|
|
287
|
-
Summary:
|
|
288
|
-
'The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.',
|
|
289
|
-
BoxOffice: '$133,698,921'
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
id: '3',
|
|
293
|
-
Title: 'The Godfather: Part II',
|
|
294
|
-
Year: 1974,
|
|
295
|
-
Summary:
|
|
296
|
-
'The early life and career of Vito Corleone in 1920s New York City is portrayed, while his son, Michael, expands and tightens his grip on the family crime syndicate.',
|
|
297
|
-
BoxOffice: '$47,542,841'
|
|
298
|
-
}
|
|
299
|
-
]}
|
|
300
|
-
/>
|
|
301
|
-
)
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
- ```javascript
|
|
305
|
-
const Example = ({ headers, rows }) => {
|
|
306
|
-
return (
|
|
307
|
-
<Responsive
|
|
308
|
-
query={{
|
|
309
|
-
small: { maxWidth: '40rem' },
|
|
310
|
-
large: { minWidth: '41rem' }
|
|
311
|
-
}}
|
|
312
|
-
props={{
|
|
313
|
-
small: { layout: 'stacked' },
|
|
314
|
-
large: { layout: 'fixed' }
|
|
315
|
-
}}
|
|
316
|
-
>
|
|
317
|
-
{({ layout }) => (
|
|
318
|
-
<div>
|
|
319
|
-
<Table caption="Top rated movies" layout={layout}>
|
|
320
|
-
<Table.Head>
|
|
321
|
-
<Table.Row>
|
|
322
|
-
{(headers || []).map(({ id, text, width, textAlign }) => (
|
|
323
|
-
<Table.ColHeader
|
|
99
|
+
```js
|
|
100
|
+
---
|
|
101
|
+
type: example
|
|
102
|
+
---
|
|
103
|
+
const Example = ({ headers, rows }) => {
|
|
104
|
+
return (
|
|
105
|
+
<Responsive
|
|
106
|
+
query={{
|
|
107
|
+
small: { maxWidth: '40rem' },
|
|
108
|
+
large: { minWidth: '41rem' }
|
|
109
|
+
}}
|
|
110
|
+
props={{
|
|
111
|
+
small: { layout: 'stacked' },
|
|
112
|
+
large: { layout: 'fixed' }
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
{({ layout }) => (
|
|
116
|
+
<div>
|
|
117
|
+
<Table caption="Top rated movies" layout={layout}>
|
|
118
|
+
<Table.Head>
|
|
119
|
+
<Table.Row>
|
|
120
|
+
{(headers || []).map(({ id, text, width, textAlign }) => (
|
|
121
|
+
<Table.ColHeader
|
|
122
|
+
key={id}
|
|
123
|
+
id={id}
|
|
124
|
+
width={width}
|
|
125
|
+
textAlign={textAlign}
|
|
126
|
+
>
|
|
127
|
+
{text}
|
|
128
|
+
</Table.ColHeader>
|
|
129
|
+
))}
|
|
130
|
+
</Table.Row>
|
|
131
|
+
</Table.Head>
|
|
132
|
+
<Table.Body>
|
|
133
|
+
{rows.map((row) => (
|
|
134
|
+
<Table.Row key={row.id}>
|
|
135
|
+
{headers.map(({ id, renderCell, textAlign }) => (
|
|
136
|
+
<Table.Cell
|
|
324
137
|
key={id}
|
|
325
|
-
|
|
326
|
-
width={width}
|
|
327
|
-
textAlign={textAlign}
|
|
138
|
+
textAlign={layout === 'stacked' ? 'start' : textAlign}
|
|
328
139
|
>
|
|
329
|
-
{
|
|
330
|
-
</Table.
|
|
140
|
+
{renderCell ? renderCell(row[id], layout) : row[id]}
|
|
141
|
+
</Table.Cell>
|
|
331
142
|
))}
|
|
332
143
|
</Table.Row>
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
</div>
|
|
350
|
-
)}
|
|
351
|
-
</Responsive>
|
|
352
|
-
)
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const renderSummary = (summary, layout) =>
|
|
356
|
-
layout === 'stacked' ? (
|
|
357
|
-
summary
|
|
358
|
-
) : (
|
|
359
|
-
<TruncateText truncate="word" ellipsis="...">
|
|
360
|
-
{summary}
|
|
361
|
-
</TruncateText>
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
render(
|
|
365
|
-
<Example
|
|
366
|
-
headers={[
|
|
367
|
-
{
|
|
368
|
-
id: 'Title',
|
|
369
|
-
text: 'Title',
|
|
370
|
-
width: '25%',
|
|
371
|
-
textAlign: 'start'
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
id: 'Year',
|
|
375
|
-
text: 'Year',
|
|
376
|
-
width: '15%',
|
|
377
|
-
textAlign: 'start'
|
|
378
|
-
},
|
|
379
|
-
{
|
|
380
|
-
id: 'Summary',
|
|
381
|
-
text: 'Summary',
|
|
382
|
-
width: '40%',
|
|
383
|
-
renderCell: renderSummary,
|
|
384
|
-
textAlign: 'start'
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
id: 'BoxOffice',
|
|
388
|
-
text: 'Box Office',
|
|
389
|
-
width: '20%',
|
|
390
|
-
textAlign: 'end'
|
|
391
|
-
}
|
|
392
|
-
]}
|
|
393
|
-
rows={[
|
|
394
|
-
{
|
|
395
|
-
id: '1',
|
|
396
|
-
Title: 'The Shawshank Redemption',
|
|
397
|
-
Year: 1994,
|
|
398
|
-
Summary:
|
|
399
|
-
'Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.',
|
|
400
|
-
BoxOffice: '$28,341,469'
|
|
401
|
-
},
|
|
402
|
-
{
|
|
403
|
-
id: '2',
|
|
404
|
-
Title: 'The Godfather',
|
|
405
|
-
Year: 1972,
|
|
406
|
-
Summary:
|
|
407
|
-
'The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.',
|
|
408
|
-
BoxOffice: '$133,698,921'
|
|
409
|
-
},
|
|
410
|
-
{
|
|
411
|
-
id: '3',
|
|
412
|
-
Title: 'The Godfather: Part II',
|
|
413
|
-
Year: 1974,
|
|
414
|
-
Summary:
|
|
415
|
-
'The early life and career of Vito Corleone in 1920s New York City is portrayed, while his son, Michael, expands and tightens his grip on the family crime syndicate.',
|
|
416
|
-
BoxOffice: '$47,542,841'
|
|
417
|
-
}
|
|
418
|
-
]}
|
|
419
|
-
/>
|
|
144
|
+
))}
|
|
145
|
+
</Table.Body>
|
|
146
|
+
</Table>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
</Responsive>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const renderSummary = (summary, layout) =>
|
|
154
|
+
layout === 'stacked' ? (
|
|
155
|
+
summary
|
|
156
|
+
) : (
|
|
157
|
+
<TruncateText truncate="word" ellipsis="...">
|
|
158
|
+
{summary}
|
|
159
|
+
</TruncateText>
|
|
420
160
|
)
|
|
421
|
-
|
|
161
|
+
|
|
162
|
+
render(
|
|
163
|
+
<Example
|
|
164
|
+
headers={[
|
|
165
|
+
{
|
|
166
|
+
id: 'Title',
|
|
167
|
+
text: 'Title',
|
|
168
|
+
width: '25%',
|
|
169
|
+
textAlign: 'start'
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'Year',
|
|
173
|
+
text: 'Year',
|
|
174
|
+
width: '15%',
|
|
175
|
+
textAlign: 'start'
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: 'Summary',
|
|
179
|
+
text: 'Summary',
|
|
180
|
+
width: '40%',
|
|
181
|
+
renderCell: renderSummary,
|
|
182
|
+
textAlign: 'start'
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: 'BoxOffice',
|
|
186
|
+
text: 'Box Office',
|
|
187
|
+
width: '20%',
|
|
188
|
+
textAlign: 'end'
|
|
189
|
+
}
|
|
190
|
+
]}
|
|
191
|
+
rows={[
|
|
192
|
+
{
|
|
193
|
+
id: '1',
|
|
194
|
+
Title: 'The Shawshank Redemption',
|
|
195
|
+
Year: 1994,
|
|
196
|
+
Summary:
|
|
197
|
+
'Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.',
|
|
198
|
+
BoxOffice: '$28,341,469'
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: '2',
|
|
202
|
+
Title: 'The Godfather',
|
|
203
|
+
Year: 1972,
|
|
204
|
+
Summary:
|
|
205
|
+
'The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.',
|
|
206
|
+
BoxOffice: '$133,698,921'
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: '3',
|
|
210
|
+
Title: 'The Godfather: Part II',
|
|
211
|
+
Year: 1974,
|
|
212
|
+
Summary:
|
|
213
|
+
'The early life and career of Vito Corleone in 1920s New York City is portrayed, while his son, Michael, expands and tightens his grip on the family crime syndicate.',
|
|
214
|
+
BoxOffice: '$47,542,841'
|
|
215
|
+
}
|
|
216
|
+
]}
|
|
217
|
+
/>
|
|
218
|
+
)
|
|
219
|
+
```
|
|
422
220
|
|
|
423
221
|
### A sortable table using our Responsive component
|
|
424
222
|
|
|
@@ -426,1146 +224,548 @@ Resize the window to see how column headers transition into a `Select` for sorti
|
|
|
426
224
|
|
|
427
225
|
By default, the options in the `Select` for sorting in stacked layout are generated from the `id` property of the `Table.ColHeader` components. If you want to display custom strings, use the `stackedSortByLabel` property.
|
|
428
226
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
})
|
|
439
|
-
|
|
440
|
-
this.state = {
|
|
441
|
-
sortBy: headers && headers[0] && headers[0].id,
|
|
442
|
-
ascending: true,
|
|
443
|
-
colTextAligns: initialColWidth
|
|
444
|
-
}
|
|
445
|
-
}
|
|
227
|
+
```js
|
|
228
|
+
---
|
|
229
|
+
type: example
|
|
230
|
+
---
|
|
231
|
+
const SortableTable = ({ caption, headers, rows }) => {
|
|
232
|
+
const initialColWidth = {}
|
|
233
|
+
headers.forEach((header) => {
|
|
234
|
+
initialColWidth[header.id] = 'start'
|
|
235
|
+
})
|
|
446
236
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
if (id === sortBy) {
|
|
451
|
-
this.setState({
|
|
452
|
-
ascending: !ascending
|
|
453
|
-
})
|
|
454
|
-
} else {
|
|
455
|
-
this.setState({
|
|
456
|
-
sortBy: id,
|
|
457
|
-
ascending: true
|
|
458
|
-
})
|
|
459
|
-
}
|
|
460
|
-
}
|
|
237
|
+
const [sortBy, setSortBy] = useState(headers && headers[0] && headers[0].id)
|
|
238
|
+
const [ascending, setAscending] = useState(true)
|
|
239
|
+
const [colTextAligns, setColTextAligns] = useState(initialColWidth)
|
|
461
240
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
colTextAligns: {
|
|
465
|
-
...state.colTextAligns,
|
|
466
|
-
[id]: value
|
|
467
|
-
}
|
|
468
|
-
}))
|
|
469
|
-
}
|
|
241
|
+
const sortedRows = useMemo(() => {
|
|
242
|
+
if (!sortBy) return rows
|
|
470
243
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
return (
|
|
476
|
-
<Table.Row>
|
|
477
|
-
{(headers || []).map(({ id, text, width }) => (
|
|
478
|
-
<Table.ColHeader
|
|
479
|
-
key={id}
|
|
480
|
-
id={id}
|
|
481
|
-
width={width}
|
|
482
|
-
{...(direction && {
|
|
483
|
-
textAlign: colTextAligns[id],
|
|
484
|
-
stackedSortByLabel: text,
|
|
485
|
-
onRequestSort: this.handleSort,
|
|
486
|
-
sortDirection: id === sortBy ? direction : 'none'
|
|
487
|
-
})}
|
|
488
|
-
>
|
|
489
|
-
{id === sortBy ? (
|
|
490
|
-
text
|
|
491
|
-
) : (
|
|
492
|
-
<>
|
|
493
|
-
<span aria-hidden="true">{text}</span>
|
|
494
|
-
<ScreenReaderContent>sort by {text}</ScreenReaderContent>
|
|
495
|
-
</>
|
|
496
|
-
)}
|
|
497
|
-
</Table.ColHeader>
|
|
498
|
-
))}
|
|
499
|
-
</Table.Row>
|
|
500
|
-
)
|
|
501
|
-
}
|
|
244
|
+
const sorted = [...rows].sort((a, b) => {
|
|
245
|
+
return a[sortBy] > b[sortBy] ? 1 : a[sortBy] < b[sortBy] ? -1 : 0
|
|
246
|
+
})
|
|
502
247
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const { colTextAligns } = this.state
|
|
248
|
+
return ascending ? sorted : sorted.reverse()
|
|
249
|
+
}, [sortBy, ascending, rows])
|
|
506
250
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
>
|
|
514
|
-
<Table caption="Set text-align for columns">
|
|
515
|
-
<Table.Head>{this.renderHeaderRow()}</Table.Head>
|
|
516
|
-
<Table.Body>
|
|
517
|
-
<Table.Row>
|
|
518
|
-
{Object.entries(colTextAligns).map(([headerId, textAlign]) => {
|
|
519
|
-
return (
|
|
520
|
-
<Table.Cell key={headerId}>
|
|
521
|
-
<RadioInputGroup
|
|
522
|
-
description={
|
|
523
|
-
<ScreenReaderContent>
|
|
524
|
-
Set text-align for column: {headerId}
|
|
525
|
-
</ScreenReaderContent>
|
|
526
|
-
}
|
|
527
|
-
name={`columnTextAlign_${headerId}`}
|
|
528
|
-
value={textAlign}
|
|
529
|
-
margin="0 0 small"
|
|
530
|
-
size="small"
|
|
531
|
-
onChange={(e, value) =>
|
|
532
|
-
this.handleColTextAlignChange(headerId, value)
|
|
533
|
-
}
|
|
534
|
-
>
|
|
535
|
-
<RadioInput label="start" value="start" />
|
|
536
|
-
<RadioInput label="center" value="center" />
|
|
537
|
-
<RadioInput label="end" value="end" />
|
|
538
|
-
</RadioInputGroup>
|
|
539
|
-
</Table.Cell>
|
|
540
|
-
)
|
|
541
|
-
})}
|
|
542
|
-
</Table.Row>
|
|
543
|
-
</Table.Body>
|
|
544
|
-
</Table>
|
|
545
|
-
</ToggleGroup>
|
|
546
|
-
)
|
|
251
|
+
const handleSort = (event, { id }) => {
|
|
252
|
+
if (id === sortBy) {
|
|
253
|
+
setAscending(!ascending)
|
|
254
|
+
} else {
|
|
255
|
+
setSortBy(id)
|
|
256
|
+
setAscending(true)
|
|
547
257
|
}
|
|
258
|
+
}
|
|
548
259
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if (a[sortBy] < b[sortBy]) {
|
|
555
|
-
return -1
|
|
556
|
-
}
|
|
557
|
-
if (a[sortBy] > b[sortBy]) {
|
|
558
|
-
return 1
|
|
559
|
-
}
|
|
560
|
-
return 0
|
|
561
|
-
})
|
|
562
|
-
|
|
563
|
-
if (!ascending) {
|
|
564
|
-
sortedRows.reverse()
|
|
565
|
-
}
|
|
566
|
-
return (
|
|
567
|
-
<Responsive
|
|
568
|
-
query={{
|
|
569
|
-
small: { maxWidth: '40rem' },
|
|
570
|
-
large: { minWidth: '41rem' }
|
|
571
|
-
}}
|
|
572
|
-
props={{
|
|
573
|
-
small: { layout: 'stacked' },
|
|
574
|
-
large: { layout: 'auto' }
|
|
575
|
-
}}
|
|
576
|
-
>
|
|
577
|
-
{(props) => (
|
|
578
|
-
<div>
|
|
579
|
-
{props.layout !== 'stacked' && (
|
|
580
|
-
<View display="block" margin="0 0 medium">
|
|
581
|
-
{this.renderOptions()}
|
|
582
|
-
</View>
|
|
583
|
-
)}
|
|
584
|
-
|
|
585
|
-
<Table
|
|
586
|
-
caption={`${caption}: sorted by ${sortBy} in ${direction} order`}
|
|
587
|
-
{...props}
|
|
588
|
-
>
|
|
589
|
-
<Table.Head renderSortLabel="Sort by">
|
|
590
|
-
{this.renderHeaderRow(direction)}
|
|
591
|
-
</Table.Head>
|
|
592
|
-
<Table.Body>
|
|
593
|
-
{sortedRows.map((row) => (
|
|
594
|
-
<Table.Row key={row.id}>
|
|
595
|
-
{headers.map(({ id, renderCell }) => (
|
|
596
|
-
<Table.Cell key={id} textAlign={colTextAligns[id]}>
|
|
597
|
-
{renderCell ? renderCell(row[id]) : row[id]}
|
|
598
|
-
</Table.Cell>
|
|
599
|
-
))}
|
|
600
|
-
</Table.Row>
|
|
601
|
-
))}
|
|
602
|
-
</Table.Body>
|
|
603
|
-
</Table>
|
|
604
|
-
<Alert
|
|
605
|
-
liveRegion={() => document.getElementById('flash-messages')}
|
|
606
|
-
liveRegionPoliteness="polite"
|
|
607
|
-
screenReaderOnly
|
|
608
|
-
>
|
|
609
|
-
{`Sorted by ${sortBy} in ${direction} order`}
|
|
610
|
-
</Alert>
|
|
611
|
-
</div>
|
|
612
|
-
)}
|
|
613
|
-
</Responsive>
|
|
614
|
-
)
|
|
615
|
-
}
|
|
260
|
+
const handleColTextAlignChange = (id, value) => {
|
|
261
|
+
setColTextAligns((prevState) => ({
|
|
262
|
+
...prevState,
|
|
263
|
+
[id]: value
|
|
264
|
+
}))
|
|
616
265
|
}
|
|
617
266
|
|
|
618
|
-
|
|
619
|
-
<
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
id
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
]}
|
|
644
|
-
rows={[
|
|
645
|
-
{
|
|
646
|
-
id: '1',
|
|
647
|
-
rank: 1,
|
|
648
|
-
title: 'The Shawshank Redemption',
|
|
649
|
-
year: 1994,
|
|
650
|
-
rating: 9.3
|
|
651
|
-
},
|
|
652
|
-
{
|
|
653
|
-
id: '2',
|
|
654
|
-
rank: 2,
|
|
655
|
-
title: 'The Godfather',
|
|
656
|
-
year: 1972,
|
|
657
|
-
rating: 9.2
|
|
658
|
-
},
|
|
659
|
-
{
|
|
660
|
-
id: '3',
|
|
661
|
-
rank: 3,
|
|
662
|
-
title: 'The Godfather: Part II',
|
|
663
|
-
year: 1974,
|
|
664
|
-
rating: 9.0
|
|
665
|
-
},
|
|
666
|
-
{
|
|
667
|
-
id: '4',
|
|
668
|
-
rank: 4,
|
|
669
|
-
title: 'The Dark Knight',
|
|
670
|
-
year: 2008,
|
|
671
|
-
rating: 9.0
|
|
672
|
-
},
|
|
673
|
-
{
|
|
674
|
-
id: '5',
|
|
675
|
-
rank: 5,
|
|
676
|
-
title: '12 Angry Men',
|
|
677
|
-
year: 1957,
|
|
678
|
-
rating: 8.9
|
|
679
|
-
}
|
|
680
|
-
]}
|
|
681
|
-
/>
|
|
267
|
+
const renderHeaderRow = (direction) => (
|
|
268
|
+
<Table.Row>
|
|
269
|
+
{(headers || []).map(({ id, text, width }) => (
|
|
270
|
+
<Table.ColHeader
|
|
271
|
+
key={id}
|
|
272
|
+
id={id}
|
|
273
|
+
width={width}
|
|
274
|
+
{...(direction && {
|
|
275
|
+
textAlign: colTextAligns[id],
|
|
276
|
+
stackedSortByLabel: text,
|
|
277
|
+
onRequestSort: handleSort,
|
|
278
|
+
sortDirection: id === sortBy ? direction : 'none'
|
|
279
|
+
})}
|
|
280
|
+
>
|
|
281
|
+
{id === sortBy ? (
|
|
282
|
+
text
|
|
283
|
+
) : (
|
|
284
|
+
<>
|
|
285
|
+
<span aria-hidden="true">{text}</span>
|
|
286
|
+
<ScreenReaderContent>sort by {text}</ScreenReaderContent>
|
|
287
|
+
</>
|
|
288
|
+
)}
|
|
289
|
+
</Table.ColHeader>
|
|
290
|
+
))}
|
|
291
|
+
</Table.Row>
|
|
682
292
|
)
|
|
683
|
-
```
|
|
684
293
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
294
|
+
const renderOptions = () => (
|
|
295
|
+
<ToggleGroup
|
|
296
|
+
size="small"
|
|
297
|
+
toggleLabel="Set text-align for columns"
|
|
298
|
+
summary="Set text-align for columns"
|
|
299
|
+
background="default"
|
|
300
|
+
>
|
|
301
|
+
<Table caption="Set text-align for columns">
|
|
302
|
+
<Table.Head>{renderHeaderRow()}</Table.Head>
|
|
303
|
+
<Table.Body>
|
|
304
|
+
<Table.Row>
|
|
305
|
+
{Object.entries(colTextAligns).map(([headerId, textAlign]) => (
|
|
306
|
+
<Table.Cell key={headerId}>
|
|
307
|
+
<RadioInputGroup
|
|
308
|
+
description={
|
|
309
|
+
<ScreenReaderContent>
|
|
310
|
+
Set text-align for column: {headerId}
|
|
311
|
+
</ScreenReaderContent>
|
|
312
|
+
}
|
|
313
|
+
name={`columnTextAlign_${headerId}`}
|
|
314
|
+
value={textAlign}
|
|
315
|
+
margin="0 0 small"
|
|
316
|
+
size="small"
|
|
317
|
+
onChange={(e, value) =>
|
|
318
|
+
handleColTextAlignChange(headerId, value)
|
|
319
|
+
}
|
|
320
|
+
>
|
|
321
|
+
<RadioInput label="start" value="start" />
|
|
322
|
+
<RadioInput label="center" value="center" />
|
|
323
|
+
<RadioInput label="end" value="end" />
|
|
324
|
+
</RadioInputGroup>
|
|
325
|
+
</Table.Cell>
|
|
326
|
+
))}
|
|
327
|
+
</Table.Row>
|
|
328
|
+
</Table.Body>
|
|
329
|
+
</Table>
|
|
330
|
+
</ToggleGroup>
|
|
331
|
+
)
|
|
714
332
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
333
|
+
const direction = ascending ? 'ascending' : 'descending'
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
<Responsive
|
|
337
|
+
query={{
|
|
338
|
+
small: { maxWidth: '40rem' },
|
|
339
|
+
large: { minWidth: '41rem' }
|
|
340
|
+
}}
|
|
341
|
+
props={{
|
|
342
|
+
small: { layout: 'stacked' },
|
|
343
|
+
large: { layout: 'auto' }
|
|
344
|
+
}}
|
|
345
|
+
>
|
|
346
|
+
{(props) => (
|
|
347
|
+
<div>
|
|
348
|
+
{props.layout !== 'stacked' && (
|
|
349
|
+
<View display="block" margin="0 0 medium">
|
|
350
|
+
{renderOptions()}
|
|
351
|
+
</View>
|
|
352
|
+
)}
|
|
721
353
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
<Table.ColHeader
|
|
726
|
-
key={id}
|
|
727
|
-
id={id}
|
|
728
|
-
width={width}
|
|
729
|
-
{...(direction && {
|
|
730
|
-
textAlign: colTextAligns[id],
|
|
731
|
-
stackedSortByLabel: text,
|
|
732
|
-
onRequestSort: handleSort,
|
|
733
|
-
sortDirection: id === sortBy ? direction : 'none'
|
|
734
|
-
})}
|
|
354
|
+
<Table
|
|
355
|
+
caption={`${caption}: sorted by ${sortBy} in ${direction} order`}
|
|
356
|
+
{...props}
|
|
735
357
|
>
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
<
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const renderOptions = () => (
|
|
750
|
-
<ToggleGroup
|
|
751
|
-
size="small"
|
|
752
|
-
toggleLabel="Set text-align for columns"
|
|
753
|
-
summary="Set text-align for columns"
|
|
754
|
-
background="default"
|
|
755
|
-
>
|
|
756
|
-
<Table caption="Set text-align for columns">
|
|
757
|
-
<Table.Head>{renderHeaderRow()}</Table.Head>
|
|
758
|
-
<Table.Body>
|
|
759
|
-
<Table.Row>
|
|
760
|
-
{Object.entries(colTextAligns).map(([headerId, textAlign]) => (
|
|
761
|
-
<Table.Cell key={headerId}>
|
|
762
|
-
<RadioInputGroup
|
|
763
|
-
description={
|
|
764
|
-
<ScreenReaderContent>
|
|
765
|
-
Set text-align for column: {headerId}
|
|
766
|
-
</ScreenReaderContent>
|
|
767
|
-
}
|
|
768
|
-
name={`columnTextAlign_${headerId}`}
|
|
769
|
-
value={textAlign}
|
|
770
|
-
margin="0 0 small"
|
|
771
|
-
size="small"
|
|
772
|
-
onChange={(e, value) =>
|
|
773
|
-
handleColTextAlignChange(headerId, value)
|
|
774
|
-
}
|
|
775
|
-
>
|
|
776
|
-
<RadioInput label="start" value="start" />
|
|
777
|
-
<RadioInput label="center" value="center" />
|
|
778
|
-
<RadioInput label="end" value="end" />
|
|
779
|
-
</RadioInputGroup>
|
|
780
|
-
</Table.Cell>
|
|
358
|
+
<Table.Head renderSortLabel="Sort by">
|
|
359
|
+
{renderHeaderRow(direction)}
|
|
360
|
+
</Table.Head>
|
|
361
|
+
<Table.Body>
|
|
362
|
+
{sortedRows.map((row) => (
|
|
363
|
+
<Table.Row key={row.id}>
|
|
364
|
+
{headers.map(({ id, renderCell }) => (
|
|
365
|
+
<Table.Cell key={id} textAlign={colTextAligns[id]}>
|
|
366
|
+
{renderCell ? renderCell(row[id]) : row[id]}
|
|
367
|
+
</Table.Cell>
|
|
368
|
+
))}
|
|
369
|
+
</Table.Row>
|
|
781
370
|
))}
|
|
782
|
-
</Table.
|
|
783
|
-
</Table
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
large: { minWidth: '41rem' }
|
|
795
|
-
}}
|
|
796
|
-
props={{
|
|
797
|
-
small: { layout: 'stacked' },
|
|
798
|
-
large: { layout: 'auto' }
|
|
799
|
-
}}
|
|
800
|
-
>
|
|
801
|
-
{(props) => (
|
|
802
|
-
<div>
|
|
803
|
-
{props.layout !== 'stacked' && (
|
|
804
|
-
<View display="block" margin="0 0 medium">
|
|
805
|
-
{renderOptions()}
|
|
806
|
-
</View>
|
|
807
|
-
)}
|
|
808
|
-
|
|
809
|
-
<Table
|
|
810
|
-
caption={`${caption}: sorted by ${sortBy} in ${direction} order`}
|
|
811
|
-
{...props}
|
|
812
|
-
>
|
|
813
|
-
<Table.Head renderSortLabel="Sort by">
|
|
814
|
-
{renderHeaderRow(direction)}
|
|
815
|
-
</Table.Head>
|
|
816
|
-
<Table.Body>
|
|
817
|
-
{sortedRows.map((row) => (
|
|
818
|
-
<Table.Row key={row.id}>
|
|
819
|
-
{headers.map(({ id, renderCell }) => (
|
|
820
|
-
<Table.Cell key={id} textAlign={colTextAligns[id]}>
|
|
821
|
-
{renderCell ? renderCell(row[id]) : row[id]}
|
|
822
|
-
</Table.Cell>
|
|
823
|
-
))}
|
|
824
|
-
</Table.Row>
|
|
825
|
-
))}
|
|
826
|
-
</Table.Body>
|
|
827
|
-
</Table>
|
|
828
|
-
<Alert
|
|
829
|
-
liveRegion={() => document.getElementById('flash-messages')}
|
|
830
|
-
liveRegionPoliteness="polite"
|
|
831
|
-
screenReaderOnly
|
|
832
|
-
>
|
|
833
|
-
{`Sorted by ${sortBy} in ${direction} order`}
|
|
834
|
-
</Alert>
|
|
835
|
-
</div>
|
|
836
|
-
)}
|
|
837
|
-
</Responsive>
|
|
838
|
-
)
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
render(
|
|
842
|
-
<SortableTable
|
|
843
|
-
caption="Top rated movies"
|
|
844
|
-
headers={[
|
|
845
|
-
{
|
|
846
|
-
id: 'rank',
|
|
847
|
-
text: 'Rank',
|
|
848
|
-
width: '15%'
|
|
849
|
-
},
|
|
850
|
-
{
|
|
851
|
-
id: 'title',
|
|
852
|
-
text: 'Title',
|
|
853
|
-
width: '55%'
|
|
854
|
-
},
|
|
855
|
-
{
|
|
856
|
-
id: 'year',
|
|
857
|
-
text: 'Year',
|
|
858
|
-
width: '15%'
|
|
859
|
-
},
|
|
860
|
-
{
|
|
861
|
-
id: 'rating',
|
|
862
|
-
text: 'Rating',
|
|
863
|
-
width: '15%',
|
|
864
|
-
renderCell: (rating) => rating.toFixed(1)
|
|
865
|
-
}
|
|
866
|
-
]}
|
|
867
|
-
rows={[
|
|
868
|
-
{
|
|
869
|
-
id: '1',
|
|
870
|
-
rank: 1,
|
|
871
|
-
title: 'The Shawshank Redemption',
|
|
872
|
-
year: 1994,
|
|
873
|
-
rating: 9.3
|
|
874
|
-
},
|
|
875
|
-
{
|
|
876
|
-
id: '2',
|
|
877
|
-
rank: 2,
|
|
878
|
-
title: 'The Godfather',
|
|
879
|
-
year: 1972,
|
|
880
|
-
rating: 9.2
|
|
881
|
-
},
|
|
882
|
-
{
|
|
883
|
-
id: '3',
|
|
884
|
-
rank: 3,
|
|
885
|
-
title: 'The Godfather: Part II',
|
|
886
|
-
year: 1974,
|
|
887
|
-
rating: 9.0
|
|
888
|
-
},
|
|
889
|
-
{
|
|
890
|
-
id: '4',
|
|
891
|
-
rank: 4,
|
|
892
|
-
title: 'The Dark Knight',
|
|
893
|
-
year: 2008,
|
|
894
|
-
rating: 9.0
|
|
895
|
-
},
|
|
896
|
-
{
|
|
897
|
-
id: '5',
|
|
898
|
-
rank: 5,
|
|
899
|
-
title: '12 Angry Men',
|
|
900
|
-
year: 1957,
|
|
901
|
-
rating: 8.9
|
|
902
|
-
}
|
|
903
|
-
]}
|
|
904
|
-
/>
|
|
371
|
+
</Table.Body>
|
|
372
|
+
</Table>
|
|
373
|
+
<Alert
|
|
374
|
+
liveRegion={() => document.getElementById('flash-messages')}
|
|
375
|
+
liveRegionPoliteness="polite"
|
|
376
|
+
screenReaderOnly
|
|
377
|
+
>
|
|
378
|
+
{`Sorted by ${sortBy} in ${direction} order`}
|
|
379
|
+
</Alert>
|
|
380
|
+
</div>
|
|
381
|
+
)}
|
|
382
|
+
</Responsive>
|
|
905
383
|
)
|
|
906
|
-
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
render(
|
|
387
|
+
<SortableTable
|
|
388
|
+
caption="Top rated movies"
|
|
389
|
+
headers={[
|
|
390
|
+
{
|
|
391
|
+
id: 'rank',
|
|
392
|
+
text: 'Rank',
|
|
393
|
+
width: '15%'
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
id: 'title',
|
|
397
|
+
text: 'Title',
|
|
398
|
+
width: '55%'
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
id: 'year',
|
|
402
|
+
text: 'Year',
|
|
403
|
+
width: '15%'
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
id: 'rating',
|
|
407
|
+
text: 'Rating',
|
|
408
|
+
width: '15%',
|
|
409
|
+
renderCell: (rating) => rating.toFixed(1)
|
|
410
|
+
}
|
|
411
|
+
]}
|
|
412
|
+
rows={[
|
|
413
|
+
{
|
|
414
|
+
id: '1',
|
|
415
|
+
rank: 1,
|
|
416
|
+
title: 'The Shawshank Redemption',
|
|
417
|
+
year: 1994,
|
|
418
|
+
rating: 9.3
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
id: '2',
|
|
422
|
+
rank: 2,
|
|
423
|
+
title: 'The Godfather',
|
|
424
|
+
year: 1972,
|
|
425
|
+
rating: 9.2
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
id: '3',
|
|
429
|
+
rank: 3,
|
|
430
|
+
title: 'The Godfather: Part II',
|
|
431
|
+
year: 1974,
|
|
432
|
+
rating: 9.0
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
id: '4',
|
|
436
|
+
rank: 4,
|
|
437
|
+
title: 'The Dark Knight',
|
|
438
|
+
year: 2008,
|
|
439
|
+
rating: 9.0
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: '5',
|
|
443
|
+
rank: 5,
|
|
444
|
+
title: '12 Angry Men',
|
|
445
|
+
year: 1957,
|
|
446
|
+
rating: 8.9
|
|
447
|
+
}
|
|
448
|
+
]}
|
|
449
|
+
/>
|
|
450
|
+
)
|
|
451
|
+
```
|
|
907
452
|
|
|
908
453
|
### A sortable table with selection and pagination
|
|
909
454
|
|
|
910
455
|
The composition order is important. `SelectableTable` -> `PaginatedTable` -> `SortableTable`, so
|
|
911
456
|
that selection does not re-paginate or re-sort the table, and pagination does not re-sort the table.
|
|
912
457
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
458
|
+
```js
|
|
459
|
+
---
|
|
460
|
+
type: example
|
|
461
|
+
---
|
|
462
|
+
const SelectableTable = ({
|
|
463
|
+
caption,
|
|
464
|
+
headers,
|
|
465
|
+
rows,
|
|
466
|
+
onSort,
|
|
467
|
+
sortBy,
|
|
468
|
+
ascending,
|
|
469
|
+
rowIds
|
|
470
|
+
}) => {
|
|
471
|
+
const [selected, setSelected] = useState(new Set())
|
|
472
|
+
|
|
473
|
+
const handleSelectAll = (allSelected) => {
|
|
474
|
+
setSelected(allSelected ? new Set() : new Set(rowIds))
|
|
475
|
+
}
|
|
924
476
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
477
|
+
const handleSelectRow = (rowSelected, rowId) => {
|
|
478
|
+
const copy = new Set(selected)
|
|
479
|
+
if (rowSelected) {
|
|
480
|
+
copy.delete(rowId)
|
|
481
|
+
} else {
|
|
482
|
+
copy.add(rowId)
|
|
928
483
|
}
|
|
484
|
+
setSelected(copy)
|
|
485
|
+
}
|
|
929
486
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
487
|
+
const allSelected =
|
|
488
|
+
selected.size > 0 && rowIds.every((id) => selected.has(id))
|
|
489
|
+
const someSelected = selected.size > 0 && !allSelected
|
|
490
|
+
const direction = ascending ? 'ascending' : 'descending'
|
|
491
|
+
|
|
492
|
+
return (
|
|
493
|
+
<Responsive
|
|
494
|
+
query={{
|
|
495
|
+
small: { maxWidth: '40rem' },
|
|
496
|
+
large: { minWidth: '41rem' }
|
|
497
|
+
}}
|
|
498
|
+
props={{
|
|
499
|
+
small: { layout: 'stacked' },
|
|
500
|
+
large: { layout: 'auto' }
|
|
501
|
+
}}
|
|
502
|
+
>
|
|
503
|
+
{(props) => (
|
|
504
|
+
<div>
|
|
505
|
+
<View as="div" padding="small" background="primary-inverse">
|
|
506
|
+
{`${selected.size} of ${rowIds.length} selected`}
|
|
507
|
+
</View>
|
|
508
|
+
<Table
|
|
509
|
+
caption={`${caption}: sorted by ${sortBy} in ${direction} order`}
|
|
510
|
+
{...props}
|
|
511
|
+
>
|
|
512
|
+
<Table.Head
|
|
513
|
+
renderSortLabel={
|
|
514
|
+
<ScreenReaderContent>Sort by</ScreenReaderContent>
|
|
515
|
+
}
|
|
516
|
+
>
|
|
517
|
+
<Table.Row>
|
|
518
|
+
<Table.ColHeader id="select">
|
|
519
|
+
<Checkbox
|
|
520
|
+
label={
|
|
521
|
+
<ScreenReaderContent>Select all</ScreenReaderContent>
|
|
522
|
+
}
|
|
523
|
+
onChange={() => handleSelectAll(allSelected)}
|
|
524
|
+
checked={allSelected}
|
|
525
|
+
indeterminate={someSelected}
|
|
526
|
+
/>
|
|
527
|
+
</Table.ColHeader>
|
|
528
|
+
{(headers || []).map(({ id, text, width }) => (
|
|
529
|
+
<Table.ColHeader
|
|
530
|
+
key={id}
|
|
531
|
+
id={id}
|
|
532
|
+
width={width}
|
|
533
|
+
onRequestSort={onSort}
|
|
534
|
+
sortDirection={id === sortBy ? direction : 'none'}
|
|
535
|
+
>
|
|
536
|
+
{id === sortBy ? (
|
|
537
|
+
text
|
|
538
|
+
) : (
|
|
539
|
+
<>
|
|
540
|
+
<span aria-hidden="true">{text}</span>
|
|
541
|
+
<ScreenReaderContent>
|
|
542
|
+
sort by {text}
|
|
543
|
+
</ScreenReaderContent>
|
|
544
|
+
</>
|
|
545
|
+
)}
|
|
546
|
+
</Table.ColHeader>
|
|
547
|
+
))}
|
|
548
|
+
</Table.Row>
|
|
549
|
+
</Table.Head>
|
|
550
|
+
<Table.Body>
|
|
551
|
+
{(rows || []).map((row) => {
|
|
552
|
+
const rowSelected = selected.has(row.id)
|
|
943
553
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
const { selected } = this.state
|
|
948
|
-
const allSelected =
|
|
949
|
-
selected.size > 0 && rowIds.every((id) => selected.has(id))
|
|
950
|
-
const someSelected = selected.size > 0 && !allSelected
|
|
951
|
-
const direction = ascending ? 'ascending' : 'descending'
|
|
952
|
-
|
|
953
|
-
return (
|
|
954
|
-
<Responsive
|
|
955
|
-
query={{
|
|
956
|
-
small: { maxWidth: '40rem' },
|
|
957
|
-
large: { minWidth: '41rem' }
|
|
958
|
-
}}
|
|
959
|
-
props={{
|
|
960
|
-
small: { layout: 'stacked' },
|
|
961
|
-
large: { layout: 'auto' }
|
|
962
|
-
}}
|
|
963
|
-
>
|
|
964
|
-
{(props) => (
|
|
965
|
-
<div>
|
|
966
|
-
<View as="div" padding="small" background="primary-inverse">
|
|
967
|
-
{`${selected.size} of ${rowIds.length} selected`}
|
|
968
|
-
</View>
|
|
969
|
-
<Table
|
|
970
|
-
caption={`${caption}: sorted by ${sortBy} in ${direction} order`}
|
|
971
|
-
{...props}
|
|
972
|
-
>
|
|
973
|
-
<Table.Head
|
|
974
|
-
renderSortLabel={
|
|
975
|
-
<ScreenReaderContent>Sort by</ScreenReaderContent>
|
|
976
|
-
}
|
|
977
|
-
>
|
|
978
|
-
<Table.Row>
|
|
979
|
-
<Table.ColHeader id="select">
|
|
554
|
+
return (
|
|
555
|
+
<Table.Row key={row.id}>
|
|
556
|
+
<Table.RowHeader>
|
|
980
557
|
<Checkbox
|
|
981
558
|
label={
|
|
982
|
-
<ScreenReaderContent>
|
|
559
|
+
<ScreenReaderContent>
|
|
560
|
+
Select row
|
|
561
|
+
</ScreenReaderContent>
|
|
983
562
|
}
|
|
984
|
-
onChange={() =>
|
|
985
|
-
checked={
|
|
986
|
-
indeterminate={someSelected}
|
|
563
|
+
onChange={() => handleSelectRow(rowSelected, row.id)}
|
|
564
|
+
checked={rowSelected}
|
|
987
565
|
/>
|
|
988
|
-
</Table.
|
|
989
|
-
{(headers || []).map(({ id,
|
|
990
|
-
<Table.
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
width={width}
|
|
994
|
-
onRequestSort={onSort}
|
|
995
|
-
sortDirection={id === sortBy ? direction : 'none'}
|
|
996
|
-
>
|
|
997
|
-
{id === sortBy ? (
|
|
998
|
-
text
|
|
999
|
-
) : (
|
|
1000
|
-
<>
|
|
1001
|
-
<span aria-hidden="true">{text}</span>
|
|
1002
|
-
<ScreenReaderContent>
|
|
1003
|
-
sort by {text}
|
|
1004
|
-
</ScreenReaderContent>
|
|
1005
|
-
</>
|
|
1006
|
-
)}
|
|
1007
|
-
</Table.ColHeader>
|
|
566
|
+
</Table.RowHeader>
|
|
567
|
+
{(headers || []).map(({ id, renderCell }) => (
|
|
568
|
+
<Table.Cell key={id}>
|
|
569
|
+
{renderCell ? renderCell(row[id]) : row[id]}
|
|
570
|
+
</Table.Cell>
|
|
1008
571
|
))}
|
|
1009
572
|
</Table.Row>
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
return (
|
|
1016
|
-
<Table.Row key={row.id}>
|
|
1017
|
-
<Table.RowHeader>
|
|
1018
|
-
<Checkbox
|
|
1019
|
-
label={
|
|
1020
|
-
<ScreenReaderContent>
|
|
1021
|
-
Select row
|
|
1022
|
-
</ScreenReaderContent>
|
|
1023
|
-
}
|
|
1024
|
-
onChange={() =>
|
|
1025
|
-
this.handleSelectRow(rowSelected, row.id)
|
|
1026
|
-
}
|
|
1027
|
-
checked={rowSelected}
|
|
1028
|
-
/>
|
|
1029
|
-
</Table.RowHeader>
|
|
1030
|
-
{(headers || []).map(({ id, renderCell }) => (
|
|
1031
|
-
<Table.Cell key={id}>
|
|
1032
|
-
{renderCell ? renderCell(row[id]) : row[id]}
|
|
1033
|
-
</Table.Cell>
|
|
1034
|
-
))}
|
|
1035
|
-
</Table.Row>
|
|
1036
|
-
)
|
|
1037
|
-
})}
|
|
1038
|
-
</Table.Body>
|
|
1039
|
-
</Table>
|
|
1040
|
-
<Alert
|
|
1041
|
-
liveRegion={() => document.getElementById('flash-messages')}
|
|
1042
|
-
liveRegionPoliteness="polite"
|
|
1043
|
-
screenReaderOnly
|
|
1044
|
-
>
|
|
1045
|
-
{`${selected.size} of ${rowIds.length} selected`}
|
|
1046
|
-
</Alert>
|
|
1047
|
-
</div>
|
|
1048
|
-
)}
|
|
1049
|
-
</Responsive>
|
|
1050
|
-
)
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
class PaginatedTable extends React.Component {
|
|
1055
|
-
constructor(props) {
|
|
1056
|
-
super(props)
|
|
1057
|
-
this.state = {
|
|
1058
|
-
page: 0
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
handleClick = (page) => {
|
|
1063
|
-
this.setState({
|
|
1064
|
-
page
|
|
1065
|
-
})
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
handleSort = (event, options) => {
|
|
1069
|
-
const { onSort } = this.props
|
|
1070
|
-
|
|
1071
|
-
this.setState({
|
|
1072
|
-
page: 0
|
|
1073
|
-
})
|
|
1074
|
-
onSort(event, options)
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
render() {
|
|
1078
|
-
const { caption, headers, rows, sortBy, ascending, perPage } = this.props
|
|
1079
|
-
const { page } = this.state
|
|
1080
|
-
const startIndex = page * perPage
|
|
1081
|
-
const slicedRows = rows.slice(startIndex, startIndex + perPage)
|
|
1082
|
-
const pageCount = perPage && Math.ceil(rows.length / perPage)
|
|
1083
|
-
|
|
1084
|
-
return (
|
|
1085
|
-
<div>
|
|
1086
|
-
<SelectableTable
|
|
1087
|
-
caption={caption}
|
|
1088
|
-
headers={headers}
|
|
1089
|
-
rows={slicedRows}
|
|
1090
|
-
onSort={this.handleSort}
|
|
1091
|
-
sortBy={sortBy}
|
|
1092
|
-
ascending={ascending}
|
|
1093
|
-
rowIds={rows.map((row) => row.id)}
|
|
1094
|
-
/>
|
|
1095
|
-
{pageCount > 1 && (
|
|
1096
|
-
<Pagination
|
|
1097
|
-
variant="compact"
|
|
1098
|
-
labelNext="Next Page"
|
|
1099
|
-
labelPrev="Previous Page"
|
|
1100
|
-
margin="large"
|
|
1101
|
-
>
|
|
1102
|
-
{Array.from(Array(pageCount), (item, index) => (
|
|
1103
|
-
<Pagination.Page
|
|
1104
|
-
key={index}
|
|
1105
|
-
onClick={() => this.handleClick(index)}
|
|
1106
|
-
current={index === page}
|
|
1107
|
-
>
|
|
1108
|
-
{index + 1}
|
|
1109
|
-
</Pagination.Page>
|
|
1110
|
-
))}
|
|
1111
|
-
</Pagination>
|
|
1112
|
-
)}
|
|
1113
|
-
<Alert
|
|
1114
|
-
liveRegion={() => document.getElementById('flash-messages')}
|
|
1115
|
-
liveRegionPoliteness="polite"
|
|
1116
|
-
screenReaderOnly
|
|
1117
|
-
>
|
|
1118
|
-
{`Table page ${page + 1} of ${pageCount}`}
|
|
1119
|
-
</Alert>
|
|
1120
|
-
</div>
|
|
1121
|
-
)
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
class SortableTable extends React.Component {
|
|
1126
|
-
constructor(props) {
|
|
1127
|
-
super(props)
|
|
1128
|
-
const { headers } = props
|
|
1129
|
-
|
|
1130
|
-
this.state = {
|
|
1131
|
-
sortBy: headers && headers[0] && headers[0].id,
|
|
1132
|
-
ascending: true
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
handleSort = (event, { id }) => {
|
|
1137
|
-
const { sortBy, ascending } = this.state
|
|
1138
|
-
|
|
1139
|
-
if (id === sortBy) {
|
|
1140
|
-
this.setState({
|
|
1141
|
-
ascending: !ascending
|
|
1142
|
-
})
|
|
1143
|
-
} else {
|
|
1144
|
-
this.setState({
|
|
1145
|
-
sortBy: id,
|
|
1146
|
-
ascending: true
|
|
1147
|
-
})
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
render() {
|
|
1152
|
-
const { caption, headers, rows, perPage } = this.props
|
|
1153
|
-
const { sortBy, ascending } = this.state
|
|
1154
|
-
const sortedRows = [...rows].sort((a, b) => {
|
|
1155
|
-
if (a[sortBy] < b[sortBy]) {
|
|
1156
|
-
return -1
|
|
1157
|
-
}
|
|
1158
|
-
if (a[sortBy] > b[sortBy]) {
|
|
1159
|
-
return 1
|
|
1160
|
-
}
|
|
1161
|
-
return 0
|
|
1162
|
-
})
|
|
1163
|
-
|
|
1164
|
-
if (!ascending) {
|
|
1165
|
-
sortedRows.reverse()
|
|
1166
|
-
}
|
|
1167
|
-
return (
|
|
1168
|
-
<div>
|
|
1169
|
-
<PaginatedTable
|
|
1170
|
-
caption={caption}
|
|
1171
|
-
headers={headers}
|
|
1172
|
-
rows={sortedRows}
|
|
1173
|
-
onSort={this.handleSort}
|
|
1174
|
-
sortBy={sortBy}
|
|
1175
|
-
ascending={ascending}
|
|
1176
|
-
perPage={perPage}
|
|
1177
|
-
/>
|
|
573
|
+
)
|
|
574
|
+
})}
|
|
575
|
+
</Table.Body>
|
|
576
|
+
</Table>
|
|
1178
577
|
<Alert
|
|
1179
578
|
liveRegion={() => document.getElementById('flash-messages')}
|
|
1180
579
|
liveRegionPoliteness="polite"
|
|
1181
580
|
screenReaderOnly
|
|
1182
581
|
>
|
|
1183
|
-
{
|
|
1184
|
-
ascending ? 'ascending' : 'descending'
|
|
1185
|
-
} order`}
|
|
582
|
+
{`${selected.size} of ${rowIds.length} selected`}
|
|
1186
583
|
</Alert>
|
|
1187
584
|
</div>
|
|
1188
|
-
)
|
|
1189
|
-
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
const renderRating = (rating) => (
|
|
1193
|
-
<Rating label="Rating" valueNow={rating} valueMax={10} iconCount={5} />
|
|
585
|
+
)}
|
|
586
|
+
</Responsive>
|
|
1194
587
|
)
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
id: 'Year',
|
|
1211
|
-
text: 'Year'
|
|
1212
|
-
},
|
|
1213
|
-
{
|
|
1214
|
-
id: 'Rating',
|
|
1215
|
-
text: 'Rating',
|
|
1216
|
-
renderCell: renderRating
|
|
1217
|
-
}
|
|
1218
|
-
]}
|
|
1219
|
-
rows={[
|
|
1220
|
-
{
|
|
1221
|
-
id: '1',
|
|
1222
|
-
Rank: 1,
|
|
1223
|
-
Title: 'The Shawshank Redemption',
|
|
1224
|
-
Year: 1994,
|
|
1225
|
-
Rating: 9.3
|
|
1226
|
-
},
|
|
1227
|
-
{
|
|
1228
|
-
id: '2',
|
|
1229
|
-
Rank: 2,
|
|
1230
|
-
Title: 'The Godfather',
|
|
1231
|
-
Year: 1972,
|
|
1232
|
-
Rating: 9.2
|
|
1233
|
-
},
|
|
1234
|
-
{
|
|
1235
|
-
id: '3',
|
|
1236
|
-
Rank: 3,
|
|
1237
|
-
Title: 'The Godfather: Part II',
|
|
1238
|
-
Year: 1974,
|
|
1239
|
-
Rating: 9.0
|
|
1240
|
-
},
|
|
1241
|
-
{
|
|
1242
|
-
id: '4',
|
|
1243
|
-
Rank: 4,
|
|
1244
|
-
Title: 'The Dark Knight',
|
|
1245
|
-
Year: 2008,
|
|
1246
|
-
Rating: 9.0
|
|
1247
|
-
},
|
|
1248
|
-
{
|
|
1249
|
-
id: '5',
|
|
1250
|
-
Rank: 5,
|
|
1251
|
-
Title: '12 Angry Men',
|
|
1252
|
-
Year: 1957,
|
|
1253
|
-
Rating: 8.9
|
|
1254
|
-
}
|
|
1255
|
-
]}
|
|
1256
|
-
perPage={3}
|
|
1257
|
-
/>
|
|
1258
|
-
)
|
|
1259
|
-
```
|
|
1260
|
-
|
|
1261
|
-
- ```javascript
|
|
1262
|
-
const SelectableTable = ({
|
|
1263
|
-
caption,
|
|
1264
|
-
headers,
|
|
1265
|
-
rows,
|
|
1266
|
-
onSort,
|
|
1267
|
-
sortBy,
|
|
1268
|
-
ascending,
|
|
1269
|
-
rowIds
|
|
1270
|
-
}) => {
|
|
1271
|
-
const [selected, setSelected] = useState(new Set())
|
|
1272
|
-
|
|
1273
|
-
const handleSelectAll = (allSelected) => {
|
|
1274
|
-
setSelected(allSelected ? new Set() : new Set(rowIds))
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
const handleSelectRow = (rowSelected, rowId) => {
|
|
1278
|
-
const copy = new Set(selected)
|
|
1279
|
-
if (rowSelected) {
|
|
1280
|
-
copy.delete(rowId)
|
|
1281
|
-
} else {
|
|
1282
|
-
copy.add(rowId)
|
|
1283
|
-
}
|
|
1284
|
-
setSelected(copy)
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
const allSelected =
|
|
1288
|
-
selected.size > 0 && rowIds.every((id) => selected.has(id))
|
|
1289
|
-
const someSelected = selected.size > 0 && !allSelected
|
|
1290
|
-
const direction = ascending ? 'ascending' : 'descending'
|
|
1291
|
-
|
|
1292
|
-
return (
|
|
1293
|
-
<Responsive
|
|
1294
|
-
query={{
|
|
1295
|
-
small: { maxWidth: '40rem' },
|
|
1296
|
-
large: { minWidth: '41rem' }
|
|
1297
|
-
}}
|
|
1298
|
-
props={{
|
|
1299
|
-
small: { layout: 'stacked' },
|
|
1300
|
-
large: { layout: 'auto' }
|
|
1301
|
-
}}
|
|
1302
|
-
>
|
|
1303
|
-
{(props) => (
|
|
1304
|
-
<div>
|
|
1305
|
-
<View as="div" padding="small" background="primary-inverse">
|
|
1306
|
-
{`${selected.size} of ${rowIds.length} selected`}
|
|
1307
|
-
</View>
|
|
1308
|
-
<Table
|
|
1309
|
-
caption={`${caption}: sorted by ${sortBy} in ${direction} order`}
|
|
1310
|
-
{...props}
|
|
1311
|
-
>
|
|
1312
|
-
<Table.Head
|
|
1313
|
-
renderSortLabel={
|
|
1314
|
-
<ScreenReaderContent>Sort by</ScreenReaderContent>
|
|
1315
|
-
}
|
|
1316
|
-
>
|
|
1317
|
-
<Table.Row>
|
|
1318
|
-
<Table.ColHeader id="select">
|
|
1319
|
-
<Checkbox
|
|
1320
|
-
label={
|
|
1321
|
-
<ScreenReaderContent>Select all</ScreenReaderContent>
|
|
1322
|
-
}
|
|
1323
|
-
onChange={() => handleSelectAll(allSelected)}
|
|
1324
|
-
checked={allSelected}
|
|
1325
|
-
indeterminate={someSelected}
|
|
1326
|
-
/>
|
|
1327
|
-
</Table.ColHeader>
|
|
1328
|
-
{(headers || []).map(({ id, text, width }) => (
|
|
1329
|
-
<Table.ColHeader
|
|
1330
|
-
key={id}
|
|
1331
|
-
id={id}
|
|
1332
|
-
width={width}
|
|
1333
|
-
onRequestSort={onSort}
|
|
1334
|
-
sortDirection={id === sortBy ? direction : 'none'}
|
|
1335
|
-
>
|
|
1336
|
-
{id === sortBy ? (
|
|
1337
|
-
text
|
|
1338
|
-
) : (
|
|
1339
|
-
<>
|
|
1340
|
-
<span aria-hidden="true">{text}</span>
|
|
1341
|
-
<ScreenReaderContent>
|
|
1342
|
-
sort by {text}
|
|
1343
|
-
</ScreenReaderContent>
|
|
1344
|
-
</>
|
|
1345
|
-
)}
|
|
1346
|
-
</Table.ColHeader>
|
|
1347
|
-
))}
|
|
1348
|
-
</Table.Row>
|
|
1349
|
-
</Table.Head>
|
|
1350
|
-
<Table.Body>
|
|
1351
|
-
{(rows || []).map((row) => {
|
|
1352
|
-
const rowSelected = selected.has(row.id)
|
|
1353
|
-
|
|
1354
|
-
return (
|
|
1355
|
-
<Table.Row key={row.id}>
|
|
1356
|
-
<Table.RowHeader>
|
|
1357
|
-
<Checkbox
|
|
1358
|
-
label={
|
|
1359
|
-
<ScreenReaderContent>
|
|
1360
|
-
Select row
|
|
1361
|
-
</ScreenReaderContent>
|
|
1362
|
-
}
|
|
1363
|
-
onChange={() => handleSelectRow(rowSelected, row.id)}
|
|
1364
|
-
checked={rowSelected}
|
|
1365
|
-
/>
|
|
1366
|
-
</Table.RowHeader>
|
|
1367
|
-
{(headers || []).map(({ id, renderCell }) => (
|
|
1368
|
-
<Table.Cell key={id}>
|
|
1369
|
-
{renderCell ? renderCell(row[id]) : row[id]}
|
|
1370
|
-
</Table.Cell>
|
|
1371
|
-
))}
|
|
1372
|
-
</Table.Row>
|
|
1373
|
-
)
|
|
1374
|
-
})}
|
|
1375
|
-
</Table.Body>
|
|
1376
|
-
</Table>
|
|
1377
|
-
<Alert
|
|
1378
|
-
liveRegion={() => document.getElementById('flash-messages')}
|
|
1379
|
-
liveRegionPoliteness="polite"
|
|
1380
|
-
screenReaderOnly
|
|
1381
|
-
>
|
|
1382
|
-
{`${selected.size} of ${rowIds.length} selected`}
|
|
1383
|
-
</Alert>
|
|
1384
|
-
</div>
|
|
1385
|
-
)}
|
|
1386
|
-
</Responsive>
|
|
1387
|
-
)
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const PaginatedTable = ({
|
|
591
|
+
caption,
|
|
592
|
+
headers,
|
|
593
|
+
rows,
|
|
594
|
+
onSort,
|
|
595
|
+
sortBy,
|
|
596
|
+
ascending,
|
|
597
|
+
perPage
|
|
598
|
+
}) => {
|
|
599
|
+
const [page, setPage] = useState(0)
|
|
600
|
+
|
|
601
|
+
const handleClick = (page) => {
|
|
602
|
+
setPage(page)
|
|
1388
603
|
}
|
|
1389
604
|
|
|
1390
|
-
const
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
onSort,
|
|
1395
|
-
sortBy,
|
|
1396
|
-
ascending,
|
|
1397
|
-
perPage
|
|
1398
|
-
}) => {
|
|
1399
|
-
const [page, setPage] = useState(0)
|
|
1400
|
-
|
|
1401
|
-
const handleClick = (page) => {
|
|
1402
|
-
setPage(page)
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
const handleSort = (event, options) => {
|
|
1406
|
-
setPage(0)
|
|
1407
|
-
onSort(event, options)
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
const startIndex = page * perPage
|
|
1411
|
-
const slicedRows = rows.slice(startIndex, startIndex + perPage)
|
|
1412
|
-
const pageCount = perPage && Math.ceil(rows.length / perPage)
|
|
605
|
+
const handleSort = (event, options) => {
|
|
606
|
+
setPage(0)
|
|
607
|
+
onSort(event, options)
|
|
608
|
+
}
|
|
1413
609
|
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
{
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
onClick={() => handleClick(index)}
|
|
1436
|
-
current={index === page}
|
|
1437
|
-
>
|
|
1438
|
-
{index + 1}
|
|
1439
|
-
</Pagination.Page>
|
|
1440
|
-
))}
|
|
1441
|
-
</Pagination>
|
|
1442
|
-
)}
|
|
1443
|
-
<Alert
|
|
1444
|
-
liveRegion={() => document.getElementById('flash-messages')}
|
|
1445
|
-
liveRegionPoliteness="polite"
|
|
1446
|
-
screenReaderOnly
|
|
610
|
+
const startIndex = page * perPage
|
|
611
|
+
const slicedRows = rows.slice(startIndex, startIndex + perPage)
|
|
612
|
+
const pageCount = perPage && Math.ceil(rows.length / perPage)
|
|
613
|
+
|
|
614
|
+
return (
|
|
615
|
+
<div>
|
|
616
|
+
<SelectableTable
|
|
617
|
+
caption={caption}
|
|
618
|
+
headers={headers}
|
|
619
|
+
rows={slicedRows}
|
|
620
|
+
onSort={handleSort}
|
|
621
|
+
sortBy={sortBy}
|
|
622
|
+
ascending={ascending}
|
|
623
|
+
rowIds={rows.map((row) => row.id)}
|
|
624
|
+
/>
|
|
625
|
+
{pageCount > 1 && (
|
|
626
|
+
<Pagination
|
|
627
|
+
variant="compact"
|
|
628
|
+
labelNext="Next Page"
|
|
629
|
+
labelPrev="Previous Page"
|
|
630
|
+
margin="large"
|
|
1447
631
|
>
|
|
1448
|
-
{
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
632
|
+
{Array.from(Array(pageCount), (item, index) => (
|
|
633
|
+
<Pagination.Page
|
|
634
|
+
key={index}
|
|
635
|
+
onClick={() => handleClick(index)}
|
|
636
|
+
current={index === page}
|
|
637
|
+
>
|
|
638
|
+
{index + 1}
|
|
639
|
+
</Pagination.Page>
|
|
640
|
+
))}
|
|
641
|
+
</Pagination>
|
|
642
|
+
)}
|
|
643
|
+
<Alert
|
|
644
|
+
liveRegion={() => document.getElementById('flash-messages')}
|
|
645
|
+
liveRegionPoliteness="polite"
|
|
646
|
+
screenReaderOnly
|
|
647
|
+
>
|
|
648
|
+
{`Table page ${page + 1} of ${pageCount}`}
|
|
649
|
+
</Alert>
|
|
650
|
+
</div>
|
|
651
|
+
)
|
|
652
|
+
}
|
|
1453
653
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
654
|
+
const SortableTable = ({ caption, headers, rows, perPage }) => {
|
|
655
|
+
const [sortBy, setSortBy] = useState(headers && headers[0] && headers[0].id)
|
|
656
|
+
const [ascending, setAscending] = useState(true)
|
|
1457
657
|
|
|
1458
|
-
|
|
1459
|
-
|
|
658
|
+
const sortedRows = useMemo(() => {
|
|
659
|
+
if (!sortBy) return rows
|
|
1460
660
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
661
|
+
const sorted = [...rows].sort((a, b) => {
|
|
662
|
+
return a[sortBy] > b[sortBy] ? 1 : a[sortBy] < b[sortBy] ? -1 : 0
|
|
663
|
+
})
|
|
1464
664
|
|
|
1465
|
-
|
|
1466
|
-
|
|
665
|
+
return ascending ? sorted : sorted.reverse()
|
|
666
|
+
}, [sortBy, ascending, rows])
|
|
1467
667
|
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
}
|
|
668
|
+
const handleSort = (event, { id }) => {
|
|
669
|
+
if (id === sortBy) {
|
|
670
|
+
setAscending(!ascending)
|
|
671
|
+
} else {
|
|
672
|
+
setSortBy(id)
|
|
673
|
+
setAscending(true)
|
|
1475
674
|
}
|
|
1476
|
-
|
|
1477
|
-
return (
|
|
1478
|
-
<div>
|
|
1479
|
-
<PaginatedTable
|
|
1480
|
-
caption={caption}
|
|
1481
|
-
headers={headers}
|
|
1482
|
-
rows={sortedRows}
|
|
1483
|
-
onSort={handleSort}
|
|
1484
|
-
sortBy={sortBy}
|
|
1485
|
-
ascending={ascending}
|
|
1486
|
-
perPage={perPage}
|
|
1487
|
-
/>
|
|
1488
|
-
<Alert
|
|
1489
|
-
liveRegion={() => document.getElementById('flash-messages')}
|
|
1490
|
-
liveRegionPoliteness="polite"
|
|
1491
|
-
screenReaderOnly
|
|
1492
|
-
>
|
|
1493
|
-
{`Sorted by ${sortBy} in ${
|
|
1494
|
-
ascending ? 'ascending' : 'descending'
|
|
1495
|
-
} order`}
|
|
1496
|
-
</Alert>
|
|
1497
|
-
</div>
|
|
1498
|
-
)
|
|
1499
675
|
}
|
|
1500
676
|
|
|
1501
|
-
|
|
1502
|
-
<
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
{
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
{
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
{
|
|
1523
|
-
id: 'Rating',
|
|
1524
|
-
text: 'Rating',
|
|
1525
|
-
renderCell: renderRating
|
|
1526
|
-
}
|
|
1527
|
-
]}
|
|
1528
|
-
rows={[
|
|
1529
|
-
{
|
|
1530
|
-
id: '1',
|
|
1531
|
-
Rank: 1,
|
|
1532
|
-
Title: 'The Shawshank Redemption',
|
|
1533
|
-
Year: 1994,
|
|
1534
|
-
Rating: 9.3
|
|
1535
|
-
},
|
|
1536
|
-
{
|
|
1537
|
-
id: '2',
|
|
1538
|
-
Rank: 2,
|
|
1539
|
-
Title: 'The Godfather',
|
|
1540
|
-
Year: 1972,
|
|
1541
|
-
Rating: 9.2
|
|
1542
|
-
},
|
|
1543
|
-
{
|
|
1544
|
-
id: '3',
|
|
1545
|
-
Rank: 3,
|
|
1546
|
-
Title: 'The Godfather: Part II',
|
|
1547
|
-
Year: 1974,
|
|
1548
|
-
Rating: 9.0
|
|
1549
|
-
},
|
|
1550
|
-
{
|
|
1551
|
-
id: '4',
|
|
1552
|
-
Rank: 4,
|
|
1553
|
-
Title: 'The Dark Knight',
|
|
1554
|
-
Year: 2008,
|
|
1555
|
-
Rating: 9.0
|
|
1556
|
-
},
|
|
1557
|
-
{
|
|
1558
|
-
id: '5',
|
|
1559
|
-
Rank: 5,
|
|
1560
|
-
Title: '12 Angry Men',
|
|
1561
|
-
Year: 1957,
|
|
1562
|
-
Rating: 8.9
|
|
1563
|
-
}
|
|
1564
|
-
]}
|
|
1565
|
-
perPage={3}
|
|
1566
|
-
/>
|
|
677
|
+
return (
|
|
678
|
+
<div>
|
|
679
|
+
<PaginatedTable
|
|
680
|
+
caption={caption}
|
|
681
|
+
headers={headers}
|
|
682
|
+
rows={sortedRows}
|
|
683
|
+
onSort={handleSort}
|
|
684
|
+
sortBy={sortBy}
|
|
685
|
+
ascending={ascending}
|
|
686
|
+
perPage={perPage}
|
|
687
|
+
/>
|
|
688
|
+
<Alert
|
|
689
|
+
liveRegion={() => document.getElementById('flash-messages')}
|
|
690
|
+
liveRegionPoliteness="polite"
|
|
691
|
+
screenReaderOnly
|
|
692
|
+
>
|
|
693
|
+
{`Sorted by ${sortBy} in ${
|
|
694
|
+
ascending ? 'ascending' : 'descending'
|
|
695
|
+
} order`}
|
|
696
|
+
</Alert>
|
|
697
|
+
</div>
|
|
1567
698
|
)
|
|
1568
|
-
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const renderRating = (rating) => (
|
|
702
|
+
<Rating label="Rating" valueNow={rating} valueMax={10} iconCount={5} />
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
render(
|
|
706
|
+
<SortableTable
|
|
707
|
+
caption="Top rated movies"
|
|
708
|
+
headers={[
|
|
709
|
+
{
|
|
710
|
+
id: 'Rank',
|
|
711
|
+
text: 'Rank'
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
id: 'Title',
|
|
715
|
+
text: 'Title',
|
|
716
|
+
width: '40%'
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
id: 'Year',
|
|
720
|
+
text: 'Year'
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
id: 'Rating',
|
|
724
|
+
text: 'Rating',
|
|
725
|
+
renderCell: renderRating
|
|
726
|
+
}
|
|
727
|
+
]}
|
|
728
|
+
rows={[
|
|
729
|
+
{
|
|
730
|
+
id: '1',
|
|
731
|
+
Rank: 1,
|
|
732
|
+
Title: 'The Shawshank Redemption',
|
|
733
|
+
Year: 1994,
|
|
734
|
+
Rating: 9.3
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
id: '2',
|
|
738
|
+
Rank: 2,
|
|
739
|
+
Title: 'The Godfather',
|
|
740
|
+
Year: 1972,
|
|
741
|
+
Rating: 9.2
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
id: '3',
|
|
745
|
+
Rank: 3,
|
|
746
|
+
Title: 'The Godfather: Part II',
|
|
747
|
+
Year: 1974,
|
|
748
|
+
Rating: 9.0
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
id: '4',
|
|
752
|
+
Rank: 4,
|
|
753
|
+
Title: 'The Dark Knight',
|
|
754
|
+
Year: 2008,
|
|
755
|
+
Rating: 9.0
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
id: '5',
|
|
759
|
+
Rank: 5,
|
|
760
|
+
Title: '12 Angry Men',
|
|
761
|
+
Year: 1957,
|
|
762
|
+
Rating: 8.9
|
|
763
|
+
}
|
|
764
|
+
]}
|
|
765
|
+
perPage={3}
|
|
766
|
+
/>
|
|
767
|
+
)
|
|
768
|
+
```
|
|
1569
769
|
|
|
1570
770
|
### Using Custom Components as Children
|
|
1571
771
|
|
|
@@ -1575,188 +775,93 @@ In some cases you might want to use custom components in a `Table`, e.g. a HOC f
|
|
|
1575
775
|
|
|
1576
776
|
Wrapper HOCs are simple, just return the original component:
|
|
1577
777
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
handleChange = (field, value) => {
|
|
1605
|
-
this.setState({
|
|
1606
|
-
[field]: value
|
|
1607
|
-
})
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
renderOptions() {
|
|
1611
|
-
const { layout, hover } = this.state
|
|
1612
|
-
|
|
1613
|
-
return (
|
|
1614
|
-
<Flex alignItems="start">
|
|
1615
|
-
<Flex.Item margin="small">
|
|
1616
|
-
<RadioInputGroup
|
|
1617
|
-
name="layout2"
|
|
1618
|
-
description="layout2"
|
|
1619
|
-
value={layout}
|
|
1620
|
-
onChange={(e, value) => this.handleChange('layout', value)}
|
|
1621
|
-
>
|
|
1622
|
-
<RadioInput label="auto" value="auto" />
|
|
1623
|
-
<RadioInput label="fixed" value="fixed" />
|
|
1624
|
-
<RadioInput label="stacked" value="stacked" />
|
|
1625
|
-
</RadioInputGroup>
|
|
1626
|
-
</Flex.Item>
|
|
1627
|
-
<Flex.Item margin="small">
|
|
1628
|
-
<Checkbox
|
|
1629
|
-
label="hover"
|
|
1630
|
-
checked={hover}
|
|
1631
|
-
onChange={(e, value) => this.handleChange('hover', !hover)}
|
|
1632
|
-
/>
|
|
1633
|
-
</Flex.Item>
|
|
1634
|
-
</Flex>
|
|
1635
|
-
)
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
render() {
|
|
1639
|
-
const { layout, hover } = this.state
|
|
1640
|
-
return (
|
|
1641
|
-
<div>
|
|
1642
|
-
{this.renderOptions()}
|
|
1643
|
-
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
1644
|
-
<Table.Head>
|
|
1645
|
-
<Table.Row>
|
|
1646
|
-
<Table.ColHeader id="Rank">Rank</Table.ColHeader>
|
|
1647
|
-
<Table.ColHeader id="Title">Title</Table.ColHeader>
|
|
1648
|
-
<Table.ColHeader id="Year">Year</Table.ColHeader>
|
|
1649
|
-
<Table.ColHeader id="Rating">Rating</Table.ColHeader>
|
|
1650
|
-
</Table.Row>
|
|
1651
|
-
</Table.Head>
|
|
1652
|
-
<Table.Body>
|
|
1653
|
-
<CustomTableRow />
|
|
1654
|
-
<Table.Row>
|
|
1655
|
-
<Table.RowHeader>2</Table.RowHeader>
|
|
1656
|
-
<Table.Cell>The Godfather</Table.Cell>
|
|
1657
|
-
<Table.Cell>1972</Table.Cell>
|
|
1658
|
-
<Table.Cell>9.2</Table.Cell>
|
|
1659
|
-
</Table.Row>
|
|
1660
|
-
<Table.Row>
|
|
1661
|
-
<Table.RowHeader>3</Table.RowHeader>
|
|
1662
|
-
<Table.Cell>The Godfather: Part II</Table.Cell>
|
|
1663
|
-
<Table.Cell>1974</Table.Cell>
|
|
1664
|
-
<Table.Cell>9.0</Table.Cell>
|
|
1665
|
-
</Table.Row>
|
|
1666
|
-
</Table.Body>
|
|
1667
|
-
</Table>
|
|
1668
|
-
</div>
|
|
1669
|
-
)
|
|
778
|
+
```js
|
|
779
|
+
---
|
|
780
|
+
type: example
|
|
781
|
+
---
|
|
782
|
+
const CustomTableCell = ({ children, ...props }) => (
|
|
783
|
+
<Table.Cell {...props}>{children}</Table.Cell>
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
const CustomTableRow = ({ children, ...props }) => (
|
|
787
|
+
<Table.Row {...props}>
|
|
788
|
+
<Table.RowHeader>1</Table.RowHeader>
|
|
789
|
+
<Table.Cell>The Shawshank Redemption</Table.Cell>
|
|
790
|
+
<Table.Cell>1994</Table.Cell>
|
|
791
|
+
<CustomTableCell>9.3</CustomTableCell>
|
|
792
|
+
</Table.Row>
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
const Example = () => {
|
|
796
|
+
const [layout, setLayout] = useState('auto')
|
|
797
|
+
const [hover, setHover] = useState(false)
|
|
798
|
+
|
|
799
|
+
const handleChange = (field, value) => {
|
|
800
|
+
if (field === 'layout') {
|
|
801
|
+
setLayout(value)
|
|
802
|
+
} else if (field === 'hover') {
|
|
803
|
+
setHover(!hover)
|
|
1670
804
|
}
|
|
1671
805
|
}
|
|
1672
806
|
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
807
|
+
const renderOptions = () => (
|
|
808
|
+
<Flex alignItems="start">
|
|
809
|
+
<Flex.Item margin="small">
|
|
810
|
+
<RadioInputGroup
|
|
811
|
+
name="layout2"
|
|
812
|
+
description="layout2"
|
|
813
|
+
value={layout}
|
|
814
|
+
onChange={(e, value) => handleChange('layout', value)}
|
|
815
|
+
>
|
|
816
|
+
<RadioInput label="auto" value="auto" />
|
|
817
|
+
<RadioInput label="fixed" value="fixed" />
|
|
818
|
+
<RadioInput label="stacked" value="stacked" />
|
|
819
|
+
</RadioInputGroup>
|
|
820
|
+
</Flex.Item>
|
|
821
|
+
<Flex.Item margin="small">
|
|
822
|
+
<Checkbox
|
|
823
|
+
label="hover"
|
|
824
|
+
checked={hover}
|
|
825
|
+
onChange={(e, value) => handleChange('hover', !hover)}
|
|
826
|
+
/>
|
|
827
|
+
</Flex.Item>
|
|
828
|
+
</Flex>
|
|
1679
829
|
)
|
|
1680
830
|
|
|
1681
|
-
|
|
1682
|
-
<
|
|
1683
|
-
|
|
1684
|
-
<Table
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
831
|
+
return (
|
|
832
|
+
<div>
|
|
833
|
+
{renderOptions()}
|
|
834
|
+
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
835
|
+
<Table.Head>
|
|
836
|
+
<Table.Row>
|
|
837
|
+
<Table.ColHeader id="Rank">Rank</Table.ColHeader>
|
|
838
|
+
<Table.ColHeader id="Title">Title</Table.ColHeader>
|
|
839
|
+
<Table.ColHeader id="Year">Year</Table.ColHeader>
|
|
840
|
+
<Table.ColHeader id="Rating">Rating</Table.ColHeader>
|
|
841
|
+
</Table.Row>
|
|
842
|
+
</Table.Head>
|
|
843
|
+
<Table.Body>
|
|
844
|
+
<CustomTableRow />
|
|
845
|
+
<Table.Row>
|
|
846
|
+
<Table.RowHeader>2</Table.RowHeader>
|
|
847
|
+
<Table.Cell>The Godfather</Table.Cell>
|
|
848
|
+
<Table.Cell>1972</Table.Cell>
|
|
849
|
+
<Table.Cell>9.2</Table.Cell>
|
|
850
|
+
</Table.Row>
|
|
851
|
+
<Table.Row>
|
|
852
|
+
<Table.RowHeader>3</Table.RowHeader>
|
|
853
|
+
<Table.Cell>The Godfather: Part II</Table.Cell>
|
|
854
|
+
<Table.Cell>1974</Table.Cell>
|
|
855
|
+
<Table.Cell>9.0</Table.Cell>
|
|
856
|
+
</Table.Row>
|
|
857
|
+
</Table.Body>
|
|
858
|
+
</Table>
|
|
859
|
+
</div>
|
|
1688
860
|
)
|
|
861
|
+
}
|
|
1689
862
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
const [hover, setHover] = useState(false)
|
|
1693
|
-
|
|
1694
|
-
const handleChange = (field, value) => {
|
|
1695
|
-
if (field === 'layout') {
|
|
1696
|
-
setLayout(value)
|
|
1697
|
-
} else if (field === 'hover') {
|
|
1698
|
-
setHover(!hover)
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
|
-
const renderOptions = () => (
|
|
1703
|
-
<Flex alignItems="start">
|
|
1704
|
-
<Flex.Item margin="small">
|
|
1705
|
-
<RadioInputGroup
|
|
1706
|
-
name="layout2"
|
|
1707
|
-
description="layout2"
|
|
1708
|
-
value={layout}
|
|
1709
|
-
onChange={(e, value) => handleChange('layout', value)}
|
|
1710
|
-
>
|
|
1711
|
-
<RadioInput label="auto" value="auto" />
|
|
1712
|
-
<RadioInput label="fixed" value="fixed" />
|
|
1713
|
-
<RadioInput label="stacked" value="stacked" />
|
|
1714
|
-
</RadioInputGroup>
|
|
1715
|
-
</Flex.Item>
|
|
1716
|
-
<Flex.Item margin="small">
|
|
1717
|
-
<Checkbox
|
|
1718
|
-
label="hover"
|
|
1719
|
-
checked={hover}
|
|
1720
|
-
onChange={(e, value) => handleChange('hover', !hover)}
|
|
1721
|
-
/>
|
|
1722
|
-
</Flex.Item>
|
|
1723
|
-
</Flex>
|
|
1724
|
-
)
|
|
1725
|
-
|
|
1726
|
-
return (
|
|
1727
|
-
<div>
|
|
1728
|
-
{renderOptions()}
|
|
1729
|
-
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
1730
|
-
<Table.Head>
|
|
1731
|
-
<Table.Row>
|
|
1732
|
-
<Table.ColHeader id="Rank">Rank</Table.ColHeader>
|
|
1733
|
-
<Table.ColHeader id="Title">Title</Table.ColHeader>
|
|
1734
|
-
<Table.ColHeader id="Year">Year</Table.ColHeader>
|
|
1735
|
-
<Table.ColHeader id="Rating">Rating</Table.ColHeader>
|
|
1736
|
-
</Table.Row>
|
|
1737
|
-
</Table.Head>
|
|
1738
|
-
<Table.Body>
|
|
1739
|
-
<CustomTableRow />
|
|
1740
|
-
<Table.Row>
|
|
1741
|
-
<Table.RowHeader>2</Table.RowHeader>
|
|
1742
|
-
<Table.Cell>The Godfather</Table.Cell>
|
|
1743
|
-
<Table.Cell>1972</Table.Cell>
|
|
1744
|
-
<Table.Cell>9.2</Table.Cell>
|
|
1745
|
-
</Table.Row>
|
|
1746
|
-
<Table.Row>
|
|
1747
|
-
<Table.RowHeader>3</Table.RowHeader>
|
|
1748
|
-
<Table.Cell>The Godfather: Part II</Table.Cell>
|
|
1749
|
-
<Table.Cell>1974</Table.Cell>
|
|
1750
|
-
<Table.Cell>9.0</Table.Cell>
|
|
1751
|
-
</Table.Row>
|
|
1752
|
-
</Table.Body>
|
|
1753
|
-
</Table>
|
|
1754
|
-
</div>
|
|
1755
|
-
)
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
render(<Example />)
|
|
1759
|
-
```
|
|
863
|
+
render(<Example />)
|
|
864
|
+
```
|
|
1760
865
|
|
|
1761
866
|
#### Fully custom components
|
|
1762
867
|
|
|
@@ -1769,211 +874,102 @@ If you want to use fully custom components you have to pay attention to the foll
|
|
|
1769
874
|
|
|
1770
875
|
Basic fully custom table:
|
|
1771
876
|
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
877
|
+
```js
|
|
878
|
+
---
|
|
879
|
+
type: example
|
|
880
|
+
---
|
|
881
|
+
const CustomTableCell = ({ children, ...props }) => (
|
|
882
|
+
<td {...props}>{children}</td>
|
|
883
|
+
)
|
|
884
|
+
|
|
885
|
+
const CustomTableRow = ({ children, ...props }) => {
|
|
886
|
+
const { hover } = useContext(TableContext)
|
|
887
|
+
const [isHovered, setIsHovered] = useState(false)
|
|
888
|
+
|
|
889
|
+
const rowStyle =
|
|
890
|
+
hover && isHovered
|
|
891
|
+
? { backgroundColor: 'SeaGreen' }
|
|
892
|
+
: { backgroundColor: 'white' }
|
|
893
|
+
|
|
894
|
+
return (
|
|
895
|
+
<tr
|
|
896
|
+
style={rowStyle}
|
|
897
|
+
onMouseOver={() => setIsHovered(true)}
|
|
898
|
+
onMouseOut={() => setIsHovered(false)}
|
|
899
|
+
>
|
|
900
|
+
{children}
|
|
901
|
+
</tr>
|
|
902
|
+
)
|
|
903
|
+
}
|
|
1782
904
|
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
toggleHoverOn = () => {
|
|
1787
|
-
this.setState({ isHovered: true })
|
|
1788
|
-
}
|
|
905
|
+
const Example = () => {
|
|
906
|
+
const [layout, setLayout] = useState('auto')
|
|
907
|
+
const [hover, setHover] = useState(false)
|
|
1789
908
|
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
return (
|
|
1796
|
-
<tr
|
|
1797
|
-
style={rowStyle}
|
|
1798
|
-
onMouseOver={this.toggleHoverOn}
|
|
1799
|
-
onMouseOut={this.toggleHoverOff}
|
|
1800
|
-
>
|
|
1801
|
-
{this.props.children}
|
|
1802
|
-
</tr>
|
|
1803
|
-
)
|
|
909
|
+
const handleChange = (field, value) => {
|
|
910
|
+
if (field === 'layout') {
|
|
911
|
+
setLayout(value)
|
|
912
|
+
} else if (field === 'hover') {
|
|
913
|
+
setHover(!hover)
|
|
1804
914
|
}
|
|
1805
915
|
}
|
|
1806
916
|
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
value={layout}
|
|
1829
|
-
onChange={(e, value) => this.handleChange('layout', value)}
|
|
1830
|
-
>
|
|
1831
|
-
<RadioInput label="auto" value="auto" />
|
|
1832
|
-
<RadioInput label="fixed" value="fixed" />
|
|
1833
|
-
</RadioInputGroup>
|
|
1834
|
-
</Flex.Item>
|
|
1835
|
-
<Flex.Item margin="small">
|
|
1836
|
-
<Checkbox
|
|
1837
|
-
label="hover"
|
|
1838
|
-
checked={hover}
|
|
1839
|
-
onChange={(e, value) => this.handleChange('hover', !hover)}
|
|
1840
|
-
/>
|
|
1841
|
-
</Flex.Item>
|
|
1842
|
-
</Flex>
|
|
1843
|
-
)
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
render() {
|
|
1847
|
-
const { layout, hover } = this.state
|
|
1848
|
-
|
|
1849
|
-
return (
|
|
1850
|
-
<div>
|
|
1851
|
-
{this.renderOptions()}
|
|
1852
|
-
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
1853
|
-
<Table.Head>
|
|
1854
|
-
<CustomTableRow>
|
|
1855
|
-
<CustomTableCell scope="col">Rank</CustomTableCell>
|
|
1856
|
-
<CustomTableCell scope="col">Title</CustomTableCell>
|
|
1857
|
-
<CustomTableCell scope="col">Year</CustomTableCell>
|
|
1858
|
-
<CustomTableCell scope="col">Rating</CustomTableCell>
|
|
1859
|
-
</CustomTableRow>
|
|
1860
|
-
</Table.Head>
|
|
1861
|
-
<Table.Body>
|
|
1862
|
-
<CustomTableRow>
|
|
1863
|
-
<CustomTableCell scope="row">1</CustomTableCell>
|
|
1864
|
-
<CustomTableCell>The Godfather</CustomTableCell>
|
|
1865
|
-
<CustomTableCell>1972</CustomTableCell>
|
|
1866
|
-
<CustomTableCell>9.2</CustomTableCell>
|
|
1867
|
-
</CustomTableRow>
|
|
1868
|
-
<CustomTableRow>
|
|
1869
|
-
<CustomTableCell scope="row">2</CustomTableCell>
|
|
1870
|
-
<CustomTableCell>The Godfather: Part II</CustomTableCell>
|
|
1871
|
-
<CustomTableCell>1974</CustomTableCell>
|
|
1872
|
-
<CustomTableCell>9.0</CustomTableCell>
|
|
1873
|
-
</CustomTableRow>
|
|
1874
|
-
</Table.Body>
|
|
1875
|
-
</Table>
|
|
1876
|
-
</div>
|
|
1877
|
-
)
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
render(<Example />)
|
|
1882
|
-
```
|
|
1883
|
-
|
|
1884
|
-
- ```javascript
|
|
1885
|
-
const CustomTableCell = ({ children, ...props }) => (
|
|
1886
|
-
<td {...props}>{children}</td>
|
|
917
|
+
const renderOptions = () => (
|
|
918
|
+
<Flex alignItems="start">
|
|
919
|
+
<Flex.Item margin="small">
|
|
920
|
+
<RadioInputGroup
|
|
921
|
+
name="Layout"
|
|
922
|
+
description="Layout"
|
|
923
|
+
value={layout}
|
|
924
|
+
onChange={(e, value) => handleChange('layout', value)}
|
|
925
|
+
>
|
|
926
|
+
<RadioInput label="auto" value="auto" />
|
|
927
|
+
<RadioInput label="fixed" value="fixed" />
|
|
928
|
+
</RadioInputGroup>
|
|
929
|
+
</Flex.Item>
|
|
930
|
+
<Flex.Item margin="small">
|
|
931
|
+
<Checkbox
|
|
932
|
+
label="hover"
|
|
933
|
+
checked={hover}
|
|
934
|
+
onChange={(e, value) => handleChange('hover', !hover)}
|
|
935
|
+
/>
|
|
936
|
+
</Flex.Item>
|
|
937
|
+
</Flex>
|
|
1887
938
|
)
|
|
1888
939
|
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
}
|
|
1920
|
-
|
|
1921
|
-
const renderOptions = () => (
|
|
1922
|
-
<Flex alignItems="start">
|
|
1923
|
-
<Flex.Item margin="small">
|
|
1924
|
-
<RadioInputGroup
|
|
1925
|
-
name="Layout"
|
|
1926
|
-
description="Layout"
|
|
1927
|
-
value={layout}
|
|
1928
|
-
onChange={(e, value) => handleChange('layout', value)}
|
|
1929
|
-
>
|
|
1930
|
-
<RadioInput label="auto" value="auto" />
|
|
1931
|
-
<RadioInput label="fixed" value="fixed" />
|
|
1932
|
-
</RadioInputGroup>
|
|
1933
|
-
</Flex.Item>
|
|
1934
|
-
<Flex.Item margin="small">
|
|
1935
|
-
<Checkbox
|
|
1936
|
-
label="hover"
|
|
1937
|
-
checked={hover}
|
|
1938
|
-
onChange={(e, value) => handleChange('hover', !hover)}
|
|
1939
|
-
/>
|
|
1940
|
-
</Flex.Item>
|
|
1941
|
-
</Flex>
|
|
1942
|
-
)
|
|
1943
|
-
|
|
1944
|
-
return (
|
|
1945
|
-
<div>
|
|
1946
|
-
{renderOptions()}
|
|
1947
|
-
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
1948
|
-
<Table.Head>
|
|
1949
|
-
<CustomTableRow>
|
|
1950
|
-
<CustomTableCell scope="col">Rank</CustomTableCell>
|
|
1951
|
-
<CustomTableCell scope="col">Title</CustomTableCell>
|
|
1952
|
-
<CustomTableCell scope="col">Year</CustomTableCell>
|
|
1953
|
-
<CustomTableCell scope="col">Rating</CustomTableCell>
|
|
1954
|
-
</CustomTableRow>
|
|
1955
|
-
</Table.Head>
|
|
1956
|
-
<Table.Body>
|
|
1957
|
-
<CustomTableRow>
|
|
1958
|
-
<CustomTableCell scope="row">1</CustomTableCell>
|
|
1959
|
-
<CustomTableCell>The Godfather</CustomTableCell>
|
|
1960
|
-
<CustomTableCell>1972</CustomTableCell>
|
|
1961
|
-
<CustomTableCell>9.2</CustomTableCell>
|
|
1962
|
-
</CustomTableRow>
|
|
1963
|
-
<CustomTableRow>
|
|
1964
|
-
<CustomTableCell scope="row">2</CustomTableCell>
|
|
1965
|
-
<CustomTableCell>The Godfather: Part II</CustomTableCell>
|
|
1966
|
-
<CustomTableCell>1974</CustomTableCell>
|
|
1967
|
-
<CustomTableCell>9.0</CustomTableCell>
|
|
1968
|
-
</CustomTableRow>
|
|
1969
|
-
</Table.Body>
|
|
1970
|
-
</Table>
|
|
1971
|
-
</div>
|
|
1972
|
-
)
|
|
1973
|
-
}
|
|
940
|
+
return (
|
|
941
|
+
<div>
|
|
942
|
+
{renderOptions()}
|
|
943
|
+
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
944
|
+
<Table.Head>
|
|
945
|
+
<CustomTableRow>
|
|
946
|
+
<CustomTableCell scope="col">Rank</CustomTableCell>
|
|
947
|
+
<CustomTableCell scope="col">Title</CustomTableCell>
|
|
948
|
+
<CustomTableCell scope="col">Year</CustomTableCell>
|
|
949
|
+
<CustomTableCell scope="col">Rating</CustomTableCell>
|
|
950
|
+
</CustomTableRow>
|
|
951
|
+
</Table.Head>
|
|
952
|
+
<Table.Body>
|
|
953
|
+
<CustomTableRow>
|
|
954
|
+
<CustomTableCell scope="row">1</CustomTableCell>
|
|
955
|
+
<CustomTableCell>The Godfather</CustomTableCell>
|
|
956
|
+
<CustomTableCell>1972</CustomTableCell>
|
|
957
|
+
<CustomTableCell>9.2</CustomTableCell>
|
|
958
|
+
</CustomTableRow>
|
|
959
|
+
<CustomTableRow>
|
|
960
|
+
<CustomTableCell scope="row">2</CustomTableCell>
|
|
961
|
+
<CustomTableCell>The Godfather: Part II</CustomTableCell>
|
|
962
|
+
<CustomTableCell>1974</CustomTableCell>
|
|
963
|
+
<CustomTableCell>9.0</CustomTableCell>
|
|
964
|
+
</CustomTableRow>
|
|
965
|
+
</Table.Body>
|
|
966
|
+
</Table>
|
|
967
|
+
</div>
|
|
968
|
+
)
|
|
969
|
+
}
|
|
1974
970
|
|
|
1975
|
-
|
|
1976
|
-
|
|
971
|
+
render(<Example />)
|
|
972
|
+
```
|
|
1977
973
|
|
|
1978
974
|
#### Fully custom components with `stacked` layout
|
|
1979
975
|
|
|
@@ -1995,269 +991,129 @@ Also you need the following props on the components:
|
|
|
1995
991
|
|
|
1996
992
|
Custom table with `stacked` layout support:
|
|
1997
993
|
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
}
|
|
2011
|
-
return (
|
|
2012
|
-
<div role="cell">
|
|
2013
|
-
{headerTxt && headerTxt}
|
|
2014
|
-
{headerTxt && ': '}
|
|
2015
|
-
{this.props.children}
|
|
2016
|
-
</div>
|
|
2017
|
-
)
|
|
2018
|
-
}
|
|
2019
|
-
return <td>{this.props.children}</td>
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
class CustomTableRow extends React.Component {
|
|
2024
|
-
static contextType = TableContext
|
|
2025
|
-
state = { isHovered: false }
|
|
2026
|
-
|
|
2027
|
-
toggleHoverOff = () => {
|
|
2028
|
-
this.setState({ isHovered: false })
|
|
2029
|
-
}
|
|
2030
|
-
toggleHoverOn = () => {
|
|
2031
|
-
this.setState({ isHovered: true })
|
|
2032
|
-
}
|
|
2033
|
-
|
|
2034
|
-
render() {
|
|
2035
|
-
const { hover, headers, isStacked } = this.context
|
|
2036
|
-
const Tag = isStacked ? 'div' : 'tr'
|
|
2037
|
-
const rowStyle =
|
|
2038
|
-
hover && this.state.isHovered
|
|
2039
|
-
? { backgroundColor: 'SeaGreen' }
|
|
2040
|
-
: { backgroundColor: 'white' }
|
|
2041
|
-
|
|
2042
|
-
return (
|
|
2043
|
-
<Tag
|
|
2044
|
-
style={rowStyle}
|
|
2045
|
-
role={isStacked ? 'row' : undefined}
|
|
2046
|
-
onMouseOver={this.toggleHoverOn}
|
|
2047
|
-
onMouseOut={this.toggleHoverOff}
|
|
2048
|
-
>
|
|
2049
|
-
{React.Children.toArray(this.props.children)
|
|
2050
|
-
.filter(React.isValidElement)
|
|
2051
|
-
.map((child, index) => {
|
|
2052
|
-
return React.cloneElement(child, {
|
|
2053
|
-
key: child.props.name,
|
|
2054
|
-
// used by `CustomTableCell` to render its column title in `stacked` layout
|
|
2055
|
-
header: headers && headers[index]
|
|
2056
|
-
})
|
|
2057
|
-
})}
|
|
2058
|
-
</Tag>
|
|
2059
|
-
)
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
|
|
2063
|
-
class Example extends React.Component {
|
|
2064
|
-
state = {
|
|
2065
|
-
layout: 'auto',
|
|
2066
|
-
hover: false
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
handleChange = (field, value) => {
|
|
2070
|
-
this.setState({
|
|
2071
|
-
[field]: value
|
|
2072
|
-
})
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
renderOptions() {
|
|
2076
|
-
const { layout, hover } = this.state
|
|
2077
|
-
|
|
2078
|
-
return (
|
|
2079
|
-
<Flex alignItems="start">
|
|
2080
|
-
<Flex.Item margin="small">
|
|
2081
|
-
<RadioInputGroup
|
|
2082
|
-
name="customStackedLayout"
|
|
2083
|
-
description="Layout"
|
|
2084
|
-
value={layout}
|
|
2085
|
-
onChange={(e, value) => this.handleChange('layout', value)}
|
|
2086
|
-
>
|
|
2087
|
-
<RadioInput label="auto" value="auto" />
|
|
2088
|
-
<RadioInput label="fixed" value="fixed" />
|
|
2089
|
-
<RadioInput label="stacked" value="stacked" />
|
|
2090
|
-
</RadioInputGroup>
|
|
2091
|
-
</Flex.Item>
|
|
2092
|
-
<Flex.Item margin="small">
|
|
2093
|
-
<Checkbox
|
|
2094
|
-
label="hover"
|
|
2095
|
-
checked={hover}
|
|
2096
|
-
onChange={(e, value) => this.handleChange('hover', !hover)}
|
|
2097
|
-
/>
|
|
2098
|
-
</Flex.Item>
|
|
2099
|
-
</Flex>
|
|
2100
|
-
)
|
|
2101
|
-
}
|
|
2102
|
-
|
|
2103
|
-
render() {
|
|
2104
|
-
const { layout, hover } = this.state
|
|
2105
|
-
|
|
2106
|
-
return (
|
|
2107
|
-
<div>
|
|
2108
|
-
{this.renderOptions()}
|
|
2109
|
-
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
2110
|
-
<Table.Head>
|
|
2111
|
-
<CustomTableRow>
|
|
2112
|
-
<CustomTableCell scope="col">Rank</CustomTableCell>
|
|
2113
|
-
<CustomTableCell scope="col">Title</CustomTableCell>
|
|
2114
|
-
<CustomTableCell scope="col">Year</CustomTableCell>
|
|
2115
|
-
<CustomTableCell scope="col">Rating</CustomTableCell>
|
|
2116
|
-
</CustomTableRow>
|
|
2117
|
-
</Table.Head>
|
|
2118
|
-
<Table.Body>
|
|
2119
|
-
<CustomTableRow>
|
|
2120
|
-
<CustomTableCell scope="row">1</CustomTableCell>
|
|
2121
|
-
<CustomTableCell>The Godfather</CustomTableCell>
|
|
2122
|
-
<CustomTableCell>1972</CustomTableCell>
|
|
2123
|
-
<CustomTableCell>9.2</CustomTableCell>
|
|
2124
|
-
</CustomTableRow>
|
|
2125
|
-
<CustomTableRow>
|
|
2126
|
-
<CustomTableCell scope="row">2</CustomTableCell>
|
|
2127
|
-
<CustomTableCell>The Godfather: Part II</CustomTableCell>
|
|
2128
|
-
<CustomTableCell>1974</CustomTableCell>
|
|
2129
|
-
<CustomTableCell>9.0</CustomTableCell>
|
|
2130
|
-
</CustomTableRow>
|
|
2131
|
-
</Table.Body>
|
|
2132
|
-
</Table>
|
|
2133
|
-
</div>
|
|
2134
|
-
)
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
render(<Example />)
|
|
2139
|
-
```
|
|
2140
|
-
|
|
2141
|
-
- ```javascript
|
|
2142
|
-
const CustomTableCell = ({ children, header }) => {
|
|
2143
|
-
const { isStacked } = useContext(TableContext)
|
|
2144
|
-
if (isStacked) {
|
|
2145
|
-
let headerTxt
|
|
2146
|
-
if (typeof header === 'function') {
|
|
2147
|
-
headerTxt = React.createElement(header)
|
|
2148
|
-
} else {
|
|
2149
|
-
headerTxt = header
|
|
2150
|
-
}
|
|
2151
|
-
return (
|
|
2152
|
-
<div role="cell">
|
|
2153
|
-
{headerTxt && headerTxt}
|
|
2154
|
-
{headerTxt && ': '}
|
|
2155
|
-
{children}
|
|
2156
|
-
</div>
|
|
2157
|
-
)
|
|
994
|
+
```js
|
|
995
|
+
---
|
|
996
|
+
type: example
|
|
997
|
+
---
|
|
998
|
+
const CustomTableCell = ({ children, header }) => {
|
|
999
|
+
const { isStacked } = useContext(TableContext)
|
|
1000
|
+
if (isStacked) {
|
|
1001
|
+
let headerTxt
|
|
1002
|
+
if (typeof header === 'function') {
|
|
1003
|
+
headerTxt = React.createElement(header)
|
|
1004
|
+
} else {
|
|
1005
|
+
headerTxt = header
|
|
2158
1006
|
}
|
|
2159
|
-
return <td>{children}</td>
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
const CustomTableRow = ({ children }) => {
|
|
2163
|
-
const { hover, headers, isStacked } = useContext(TableContext)
|
|
2164
|
-
const [isHovered, setIsHovered] = useState(false)
|
|
2165
|
-
|
|
2166
|
-
const Tag = isStacked ? 'div' : 'tr'
|
|
2167
|
-
const rowStyle =
|
|
2168
|
-
hover && isHovered
|
|
2169
|
-
? { backgroundColor: 'SeaGreen' }
|
|
2170
|
-
: { backgroundColor: 'white' }
|
|
2171
|
-
|
|
2172
1007
|
return (
|
|
2173
|
-
<
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
>
|
|
2179
|
-
{React.Children.toArray(children)
|
|
2180
|
-
.filter(React.isValidElement)
|
|
2181
|
-
.map((child, index) => {
|
|
2182
|
-
return React.cloneElement(child, {
|
|
2183
|
-
key: child.props.name,
|
|
2184
|
-
// used by `CustomTableCell` to render its column title in `stacked` layout
|
|
2185
|
-
header: headers && headers[index]
|
|
2186
|
-
})
|
|
2187
|
-
})}
|
|
2188
|
-
</Tag>
|
|
1008
|
+
<div role="cell">
|
|
1009
|
+
{headerTxt && headerTxt}
|
|
1010
|
+
{headerTxt && ': '}
|
|
1011
|
+
{children}
|
|
1012
|
+
</div>
|
|
2189
1013
|
)
|
|
2190
1014
|
}
|
|
1015
|
+
return <td>{children}</td>
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
const CustomTableRow = ({ children }) => {
|
|
1019
|
+
const { hover, headers, isStacked } = useContext(TableContext)
|
|
1020
|
+
const [isHovered, setIsHovered] = useState(false)
|
|
1021
|
+
|
|
1022
|
+
const Tag = isStacked ? 'div' : 'tr'
|
|
1023
|
+
const rowStyle =
|
|
1024
|
+
hover && isHovered
|
|
1025
|
+
? { backgroundColor: 'SeaGreen' }
|
|
1026
|
+
: { backgroundColor: 'white' }
|
|
1027
|
+
|
|
1028
|
+
return (
|
|
1029
|
+
<Tag
|
|
1030
|
+
style={rowStyle}
|
|
1031
|
+
role={isStacked ? 'row' : undefined}
|
|
1032
|
+
onMouseOver={() => setIsHovered(true)}
|
|
1033
|
+
onMouseOut={() => setIsHovered(false)}
|
|
1034
|
+
>
|
|
1035
|
+
{React.Children.toArray(children)
|
|
1036
|
+
.filter(React.isValidElement)
|
|
1037
|
+
.map((child, index) => {
|
|
1038
|
+
return React.cloneElement(child, {
|
|
1039
|
+
key: child.props.name,
|
|
1040
|
+
// used by `CustomTableCell` to render its column title in `stacked` layout
|
|
1041
|
+
header: headers && headers[index]
|
|
1042
|
+
})
|
|
1043
|
+
})}
|
|
1044
|
+
</Tag>
|
|
1045
|
+
)
|
|
1046
|
+
}
|
|
2191
1047
|
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
1048
|
+
const Example = () => {
|
|
1049
|
+
const [layout, setLayout] = useState('auto')
|
|
1050
|
+
const [hover, setHover] = useState(false)
|
|
2195
1051
|
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
}
|
|
1052
|
+
const handleChange = (field, value) => {
|
|
1053
|
+
if (field === 'layout') {
|
|
1054
|
+
setLayout(value)
|
|
1055
|
+
} else if (field === 'hover') {
|
|
1056
|
+
setHover(!hover)
|
|
2202
1057
|
}
|
|
1058
|
+
}
|
|
2203
1059
|
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
1060
|
+
const renderOptions = () => (
|
|
1061
|
+
<Flex alignItems="start">
|
|
1062
|
+
<Flex.Item margin="small">
|
|
1063
|
+
<RadioInputGroup
|
|
1064
|
+
name="customStackedLayout"
|
|
1065
|
+
description="Layout"
|
|
1066
|
+
value={layout}
|
|
1067
|
+
onChange={(e, value) => handleChange('layout', value)}
|
|
1068
|
+
>
|
|
1069
|
+
<RadioInput label="auto" value="auto" />
|
|
1070
|
+
<RadioInput label="fixed" value="fixed" />
|
|
1071
|
+
<RadioInput label="stacked" value="stacked" />
|
|
1072
|
+
</RadioInputGroup>
|
|
1073
|
+
</Flex.Item>
|
|
1074
|
+
<Flex.Item margin="small">
|
|
1075
|
+
<Checkbox
|
|
1076
|
+
label="hover"
|
|
1077
|
+
checked={hover}
|
|
1078
|
+
onChange={(e, value) => handleChange('hover', !hover)}
|
|
1079
|
+
/>
|
|
1080
|
+
</Flex.Item>
|
|
1081
|
+
</Flex>
|
|
1082
|
+
)
|
|
2227
1083
|
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
1084
|
+
return (
|
|
1085
|
+
<div>
|
|
1086
|
+
{renderOptions()}
|
|
1087
|
+
<Table caption="Top rated movies" layout={layout} hover={hover}>
|
|
1088
|
+
<Table.Head>
|
|
1089
|
+
<CustomTableRow>
|
|
1090
|
+
<CustomTableCell scope="col">Rank</CustomTableCell>
|
|
1091
|
+
<CustomTableCell scope="col">Title</CustomTableCell>
|
|
1092
|
+
<CustomTableCell scope="col">Year</CustomTableCell>
|
|
1093
|
+
<CustomTableCell scope="col">Rating</CustomTableCell>
|
|
1094
|
+
</CustomTableRow>
|
|
1095
|
+
</Table.Head>
|
|
1096
|
+
<Table.Body>
|
|
1097
|
+
<CustomTableRow>
|
|
1098
|
+
<CustomTableCell scope="row">1</CustomTableCell>
|
|
1099
|
+
<CustomTableCell>The Godfather</CustomTableCell>
|
|
1100
|
+
<CustomTableCell>1972</CustomTableCell>
|
|
1101
|
+
<CustomTableCell>9.2</CustomTableCell>
|
|
1102
|
+
</CustomTableRow>
|
|
1103
|
+
<CustomTableRow>
|
|
1104
|
+
<CustomTableCell scope="row">2</CustomTableCell>
|
|
1105
|
+
<CustomTableCell>The Godfather: Part II</CustomTableCell>
|
|
1106
|
+
<CustomTableCell>1974</CustomTableCell>
|
|
1107
|
+
<CustomTableCell>9.0</CustomTableCell>
|
|
1108
|
+
</CustomTableRow>
|
|
1109
|
+
</Table.Body>
|
|
1110
|
+
</Table>
|
|
1111
|
+
</div>
|
|
1112
|
+
)
|
|
1113
|
+
}
|
|
2258
1114
|
|
|
2259
|
-
|
|
2260
|
-
|
|
1115
|
+
render(<Example />)
|
|
1116
|
+
```
|
|
2261
1117
|
|
|
2262
1118
|
### Guidelines
|
|
2263
1119
|
|