|
| 1 | +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | +import React, { useMemo, useState } from 'react'; |
| 4 | + |
| 5 | +import Box from '~components/box'; |
| 6 | +import Header from '~components/header'; |
| 7 | +import SegmentedControl from '~components/segmented-control'; |
| 8 | +import SpaceBetween from '~components/space-between'; |
| 9 | +import TableBody from '~components/table-body'; |
| 10 | +import TableBodyCell from '~components/table-body-cell'; |
| 11 | +import TableHead from '~components/table-head'; |
| 12 | +import TableHeaderCell from '~components/table-header-cell'; |
| 13 | +import TableRoot, { TableRootProps } from '~components/table-root'; |
| 14 | +import TableRow from '~components/table-row'; |
| 15 | + |
| 16 | +// Sticky header in both layout modes. The Name header carries a sort button so the pinned copy's |
| 17 | +// pointer interaction can be exercised. |
| 18 | + |
| 19 | +const GRID_COLUMNS: ReadonlyArray<TableRootProps.ColumnDefinition> = [ |
| 20 | + { size: 260 }, |
| 21 | + { size: 260 }, |
| 22 | + { size: 260 }, |
| 23 | + { size: 260 }, |
| 24 | + { size: 260 }, |
| 25 | +]; |
| 26 | +const ROW_COUNT = 60; |
| 27 | + |
| 28 | +interface Row { |
| 29 | + id: string; |
| 30 | + name: string; |
| 31 | + type: string; |
| 32 | + size: string; |
| 33 | + status: string; |
| 34 | + region: string; |
| 35 | +} |
| 36 | + |
| 37 | +const makeRows = (n: number): Row[] => |
| 38 | + Array.from({ length: n }, (_, index) => ({ |
| 39 | + id: `row-${index}`, |
| 40 | + name: `Resource ${String(index).padStart(3, '0')}`, |
| 41 | + type: index % 3 === 0 ? 'Compute' : index % 3 === 1 ? 'Storage' : 'Network', |
| 42 | + size: `${(index % 8) + 1} GiB`, |
| 43 | + status: index % 2 === 0 ? 'Available' : 'Pending', |
| 44 | + region: index % 2 === 0 ? 'us-east-1' : 'eu-west-1', |
| 45 | + })); |
| 46 | + |
| 47 | +export default function TableStickyHeaderPage() { |
| 48 | + const [layout, setLayout] = useState<'grid' | 'auto'>('grid'); |
| 49 | + const [ascending, setAscending] = useState(true); |
| 50 | + const rows = useMemo(() => { |
| 51 | + const direction = ascending ? 1 : -1; |
| 52 | + return makeRows(ROW_COUNT).sort((a, b) => direction * a.name.localeCompare(b.name)); |
| 53 | + }, [ascending]); |
| 54 | + |
| 55 | + const columnLayout: TableRootProps.ColumnLayout = |
| 56 | + layout === 'grid' ? { type: 'grid', columns: GRID_COLUMNS } : { type: 'auto' }; |
| 57 | + |
| 58 | + return ( |
| 59 | + <Box padding="l"> |
| 60 | + <SpaceBetween size="l"> |
| 61 | + <Box variant="h1">Table atomics — sticky header</Box> |
| 62 | + <Box color="text-body-secondary"> |
| 63 | + Scroll the page down — the header pins to the top. Scroll the table sideways — the pinned header tracks the |
| 64 | + columns. Switch between grid and auto layout to exercise both alignment paths. |
| 65 | + </Box> |
| 66 | + <SegmentedControl |
| 67 | + selectedId={layout} |
| 68 | + onChange={({ detail }) => setLayout(detail.selectedId as 'grid' | 'auto')} |
| 69 | + label="Column layout" |
| 70 | + options={[ |
| 71 | + { id: 'grid', text: 'Grid layout' }, |
| 72 | + { id: 'auto', text: 'Auto layout' }, |
| 73 | + ]} |
| 74 | + /> |
| 75 | + <Header counter={`(${rows.length})`}>Resources</Header> |
| 76 | + <TableRoot |
| 77 | + columnLayout={columnLayout} |
| 78 | + ariaLabel="Resources" |
| 79 | + stickyHeader={ |
| 80 | + <TableHead> |
| 81 | + <TableRow> |
| 82 | + <TableHeaderCell ariaSort={ascending ? 'ascending' : 'descending'}> |
| 83 | + <button |
| 84 | + type="button" |
| 85 | + onClick={() => setAscending(value => !value)} |
| 86 | + style={{ font: 'inherit', background: 'none', border: 0, padding: 0, cursor: 'pointer' }} |
| 87 | + > |
| 88 | + Name {ascending ? '▲' : '▼'} |
| 89 | + </button> |
| 90 | + </TableHeaderCell> |
| 91 | + <TableHeaderCell>Type</TableHeaderCell> |
| 92 | + <TableHeaderCell>Size</TableHeaderCell> |
| 93 | + <TableHeaderCell>Status</TableHeaderCell> |
| 94 | + <TableHeaderCell>Region</TableHeaderCell> |
| 95 | + </TableRow> |
| 96 | + </TableHead> |
| 97 | + } |
| 98 | + > |
| 99 | + <TableBody> |
| 100 | + {rows.map(row => ( |
| 101 | + <TableRow key={row.id}> |
| 102 | + <TableBodyCell>{row.name}</TableBodyCell> |
| 103 | + <TableBodyCell>{row.type}</TableBodyCell> |
| 104 | + <TableBodyCell>{row.size}</TableBodyCell> |
| 105 | + <TableBodyCell>{row.status}</TableBodyCell> |
| 106 | + <TableBodyCell>{row.region}</TableBodyCell> |
| 107 | + </TableRow> |
| 108 | + ))} |
| 109 | + </TableBody> |
| 110 | + </TableRoot> |
| 111 | + </SpaceBetween> |
| 112 | + </Box> |
| 113 | + ); |
| 114 | +} |
0 commit comments