2023-01-31 18:08:07 -07:00
|
|
|
import React from "react";
|
|
|
|
|
import _ from "lodash";
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
import {Table} from "./ui";
|
2023-09-12 22:03:51 -06:00
|
|
|
import {TextPlaceholder} from "./Placeholder";
|
2023-01-31 18:08:07 -07:00
|
|
|
|
|
|
|
|
export class SortableTable extends React.Component {
|
|
|
|
|
constructor(props) {
|
|
|
|
|
super(props);
|
2023-05-29 18:04:03 -06:00
|
|
|
this.state = {
|
|
|
|
|
sortColumn: props.defaultSortColumn || null,
|
|
|
|
|
direction: props.defaultDirection || 'ascending',
|
|
|
|
|
};
|
2023-01-31 18:08:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changeSort = (key) => {
|
|
|
|
|
const {sortColumn, direction} = this.state;
|
|
|
|
|
if (key === sortColumn) {
|
|
|
|
|
this.setState({direction: direction === 'ascending' ? 'descending' : 'ascending'});
|
|
|
|
|
} else {
|
|
|
|
|
this.setState({sortColumn: key, direction: 'ascending'});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sortData = (data) => {
|
|
|
|
|
let {tableHeaders, defaultSortColumn} = this.props;
|
|
|
|
|
let {sortColumn, direction} = this.state;
|
|
|
|
|
|
|
|
|
|
const sortKey = sortColumn || defaultSortColumn;
|
|
|
|
|
const sortHeader = _.find(tableHeaders, {key: sortKey});
|
|
|
|
|
if (sortHeader === undefined) {
|
|
|
|
|
// No sort header, probably no default defined.
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
data = _.sortBy(data, sortHeader['sortBy']);
|
|
|
|
|
data = direction === 'descending' ? data.reverse() : data;
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
let {rowFunc, tableHeaders, rowKey} = this.props;
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
let {direction, sortColumn} = this.state;
|
2023-01-31 18:08:07 -07:00
|
|
|
rowKey = rowKey || 'key';
|
|
|
|
|
|
2023-09-12 22:03:51 -06:00
|
|
|
const data = this.props['data'] ? this.sortData(this.props['data']) : null;
|
2023-01-31 18:08:07 -07:00
|
|
|
|
|
|
|
|
const tableHeader = (spec) => {
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
const {key, text, sortBy} = spec;
|
2023-01-31 18:08:07 -07:00
|
|
|
if (sortBy) {
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
const active = sortColumn === key;
|
|
|
|
|
return <Table.HeaderCell
|
2023-01-31 18:08:07 -07:00
|
|
|
key={key}
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
sorted={active ? direction : null}
|
|
|
|
|
onSort={() => this.changeSort(key)}
|
2023-01-31 18:08:07 -07:00
|
|
|
>
|
|
|
|
|
{text}
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
</Table.HeaderCell>;
|
2023-01-31 18:08:07 -07:00
|
|
|
} else {
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
return <Table.HeaderCell key={key}>{text}</Table.HeaderCell>
|
2023-01-31 18:08:07 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-12 22:03:51 -06:00
|
|
|
// Use placeholder while data is null.
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
let rows = <Table.Row><Table.Cell colSpan={tableHeaders.length}><TextPlaceholder/></Table.Cell></Table.Row>;
|
2023-09-12 22:03:51 -06:00
|
|
|
if (data !== null) {
|
2024-01-01 17:10:51 -07:00
|
|
|
if (data.length > 0) {
|
|
|
|
|
// Convert data to table rows.
|
|
|
|
|
rows = data.map(i => <React.Fragment key={i[rowKey]}>{rowFunc(i, this.sortData)}</React.Fragment>);
|
|
|
|
|
} else {
|
|
|
|
|
// No results in data.
|
|
|
|
|
rows = this.props.emptyRow
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
|| <Table.Row><Table.Cell colSpan={tableHeaders.length}>No results</Table.Cell></Table.Row>;
|
2024-01-01 17:10:51 -07:00
|
|
|
}
|
2023-09-12 22:03:51 -06:00
|
|
|
}
|
2023-01-31 18:08:07 -07:00
|
|
|
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
return <Table {...this.props.tableProps}>
|
|
|
|
|
<Table.Header>
|
|
|
|
|
<Table.Row>
|
2023-01-31 18:08:07 -07:00
|
|
|
{tableHeaders.map(tableHeader)}
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
</Table.Row>
|
|
|
|
|
</Table.Header>
|
|
|
|
|
<Table.Body>
|
2023-01-31 18:08:07 -07:00
|
|
|
{rows}
|
ui: nine leaf files off Semantic, and the components they needed
Phase 2, part 2. Baseline 72 -> 63.
Migrated: Tables, SortableTable, DonatePage, Share, Headline, ErrorBoundary,
and the QR code, VIN decoder, and temperature calculators.
New in the library, all in the gallery and tested:
SearchBox Semantic's `<Search category>`, keeping its grouped `results`
shape because four call sites already build it. A real
combobox: arrows move, Enter takes the highlighted suggestion
or submits the raw text, Escape closes without clearing.
Pagination Semantic's prop names, but onPageChange gets the page number
rather than an (event, data) pair.
TabBar the strip and its house style only; routing stays at the call
site, which is what decides what is active.
IconStack two glyphs composed into one symbol, named once for assistive
technology (Semantic's IconGroup).
Table.HeaderCell gains `sorted` and `onSort`.
That last one is a fix, not just a port. Semantic's `sorted` prop set
`aria-sort`; migrating a table by hand dropped it, leaving a clickable <th>
that no keyboard could reach and no screen reader could describe. Putting it
in the library means the next table gets it right without anyone remembering.
Night mode holes closed along the way: the QR codes on the donate, share, and
calculator pages are white by necessity — a scanner needs the contrast — and
they render as SVG, which the media filter does not target. In night mode that
was a lit panel. They now carry `.media`, so the filter remaps the block as a
unit and the contrast survives the mapping.
Also: `paypal` joins the icon map rather than being imported ad hoc in a page,
and FileBrowser stops passing Semantic-only table props into a DOM element.
2026-07-30 09:31:15 -06:00
|
|
|
</Table.Body>
|
2023-01-31 18:08:07 -07:00
|
|
|
{this.props.footer}
|
|
|
|
|
</Table>
|
|
|
|
|
}
|
|
|
|
|
}
|