diff --git a/docs/src/pages/docs/api/useSortBy.md b/docs/src/pages/docs/api/useSortBy.md index f2ba1acbdb..32a7170e1f 100644 --- a/docs/src/pages/docs/api/useSortBy.md +++ b/docs/src/pages/docs/api/useSortBy.md @@ -75,7 +75,7 @@ The following options are supported on any `Column` object passed to the `column - `sortType: String | Function(rowA: , rowB: , columnId: String, desc: Bool)` - Used to compare 2 rows of data and order them correctly. - If a **function** is passed, it must be **memoized**. The sortType function should return 1 if rowA is larger, and -1 if rowB is larger. `react-table` will take care of the rest. - - String options: `basic`, `datetime`, `alphanumeric`. Defaults to `alphanumeric`. + - String options: `string`, `number`, `basic`, `datetime`, `alphanumeric`. Defaults to `alphanumeric`. - The resolved function from the this string/function will be used to sort the this column's data. - If a `string` is passed, the function with that name located on either the custom `sortTypes` option or the built-in sorting types object will be used. - If a `function` is passed, it will be used. diff --git a/src/plugin-hooks/tests/useSortBy.test.js b/src/plugin-hooks/tests/useSortBy.test.js index 86dd6d9939..39f9ca9af6 100644 --- a/src/plugin-hooks/tests/useSortBy.test.js +++ b/src/plugin-hooks/tests/useSortBy.test.js @@ -155,6 +155,7 @@ function App({ useTableRef, initialState }) { { Header: 'First Name', accessor: 'firstName', + sortType: 'string' }, { Header: 'Last Name', @@ -168,6 +169,7 @@ function App({ useTableRef, initialState }) { { Header: 'Age', accessor: 'age', + sortType: 'number' }, { Header: 'Visits', diff --git a/src/sortTypes.js b/src/sortTypes.js index d7cfe3e53d..7efcd6c71e 100644 --- a/src/sortTypes.js +++ b/src/sortTypes.js @@ -4,8 +4,8 @@ const reSplitAlphaNumeric = /([0-9]+)/gm // It handles numbers, mixed alphanumeric combinations, and even // null, undefined, and Infinity export const alphanumeric = (rowA, rowB, columnId) => { - let a = getRowValueByColumnID(rowA, columnId) - let b = getRowValueByColumnID(rowB, columnId) + let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId) + // Force to strings (or "" for unsupported types) a = toString(a) b = toString(b) @@ -52,10 +52,8 @@ export const alphanumeric = (rowA, rowB, columnId) => { return a.length - b.length } - export function datetime(rowA, rowB, columnId) { - let a = getRowValueByColumnID(rowA, columnId) - let b = getRowValueByColumnID(rowB, columnId) + let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId) a = a.getTime() b = b.getTime() @@ -64,8 +62,51 @@ export function datetime(rowA, rowB, columnId) { } export function basic(rowA, rowB, columnId) { - let a = getRowValueByColumnID(rowA, columnId) - let b = getRowValueByColumnID(rowB, columnId) + let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId) + + return compareBasic(a, b) +} + +export function string(rowA, rowB, columnId) { + let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId) + + a = a.split('').filter(Boolean) + b = b.split('').filter(Boolean) + + while (a.length && b.length) { + let aa = a.shift() + let bb = b.shift() + + let alower = aa.toLowerCase() + let blower = bb.toLowerCase() + + // Case insensitive comparison until characters match + if (alower > blower) { + return 1 + } + if (blower > alower) { + return -1 + } + // If lowercase characters are identical + if (aa > bb) { + return 1 + } + if (bb > aa) { + return -1 + } + continue + } + + return a.length - b.length +} + +export function number(rowA, rowB, columnId) { + let [a, b] = getRowValuesByColumnID(rowA, rowB, columnId) + + const replaceNonNumeric = /[^0-9.]/gi + + a = Number(String(a).replace(replaceNonNumeric, '')) + b = Number(String(b).replace(replaceNonNumeric, '')) return compareBasic(a, b) } @@ -76,8 +117,8 @@ function compareBasic(a, b) { return a === b ? 0 : a > b ? 1 : -1 } -function getRowValueByColumnID(row, columnId) { - return row.values[columnId] +function getRowValuesByColumnID(row1, row2, columnId) { + return [row1.values[columnId], row2.values[columnId]] } function toString(a) {