diff --git a/app/editor/src/features/content/list-view/hooks/useColumns.tsx b/app/editor/src/features/content/list-view/hooks/useColumns.tsx index 512e0f82a9..294fee93e2 100644 --- a/app/editor/src/features/content/list-view/hooks/useColumns.tsx +++ b/app/editor/src/features/content/list-view/hooks/useColumns.tsx @@ -20,6 +20,8 @@ import { WorkOrderStatusName, } from 'tno-core'; +import { naturalSortValue } from '../utils/naturalSort'; + export interface IColumnProps { fetch: ( filter: IContentListFilter & Partial, @@ -67,11 +69,7 @@ export const useColumns = ({ fetch }: IColumnProps): ITableHookColumn { - return `${row.original.page ? row.original.page : ''}:${ - row.original.section ? row.original.section : '' - }`; - }, + sort: (row) => naturalSortValue(row.original), label: ( Page:Section diff --git a/app/editor/src/features/content/list-view/utils/naturalSort.ts b/app/editor/src/features/content/list-view/utils/naturalSort.ts new file mode 100644 index 0000000000..ed4430937c --- /dev/null +++ b/app/editor/src/features/content/list-view/utils/naturalSort.ts @@ -0,0 +1,23 @@ +import { IContentSearchResult } from 'store/slices'; + +function getPageSectionValue(row: IContentSearchResult) { + // gnerate page:section string, keep it in the lowercase + const value = `${row.original.page ? row.original.page : ''}:${ + row.original.section ? row.original.section : '' + }`.toLowerCase(); + return value; +} + +export function naturalSortValue(row: IContentSearchResult) { + const pageSectionValue = getPageSectionValue(row); + // Replace each segment of digits and non-digits with formatted strings + // Digits are padded with zeros to the left to ensure correct natural sorting + // Non-digits are left as is + // eq. 'A2:sport' -> 'A0000000002:sport' + // eq. 'A02:sport' -> 'A0000000002:sport' + const formattedPageSectionValue = pageSectionValue.replace(/(\d+)|(\D+)/g, (_, $1, $2) => + $1 ? Number($1).toString().padStart(10, '0') : $2, + ); + // we do consider source as the primary key for sorting + return formattedPageSectionValue; +} diff --git a/app/editor/src/features/content/papers/hooks/useColumns.tsx b/app/editor/src/features/content/papers/hooks/useColumns.tsx index f92d5d7b46..7345f349e5 100644 --- a/app/editor/src/features/content/papers/hooks/useColumns.tsx +++ b/app/editor/src/features/content/papers/hooks/useColumns.tsx @@ -2,6 +2,7 @@ import { Status } from 'components/status'; import { TabControl } from 'components/tab-control'; import { AdvancedSearchKeys } from 'features/content/constants'; import { IContentListAdvancedFilter, IContentListFilter } from 'features/content/interfaces'; +import { naturalSortValue } from 'features/content/list-view/utils/naturalSort'; import { useContent } from 'store/hooks'; import { IContentSearchResult } from 'store/slices'; import { CellEllipsis, Checkbox, ITableHookColumn, LogicalOperator, Page, Row } from 'tno-core'; @@ -45,11 +46,7 @@ export const useColumns = ({ }, { accessor: 'section', - sort: (row) => { - return `${row.original.page ? row.original.page : ''}:${ - row.original.section ? row.original.section : '' - }`; - }, + sort: (row) => naturalSortValue(row.original), label: ( Page:Section diff --git a/libs/npm/core/src/components/table/FlexboxTable.tsx b/libs/npm/core/src/components/table/FlexboxTable.tsx index e0a0a449a2..36f49f6214 100644 --- a/libs/npm/core/src/components/table/FlexboxTable.tsx +++ b/libs/npm/core/src/components/table/FlexboxTable.tsx @@ -4,7 +4,7 @@ import { Container } from '../container'; import { Text } from '../form'; import { getSortId, ITableProps, SortFlag, TablePager, useTable } from '.'; import * as styled from './styled'; - +import { determineSortValue } from './utils/determineSort'; export const FlexboxTable = ({ rowId, columns, @@ -109,10 +109,7 @@ export const FlexboxTable = ({ { id: getSortId(col, index), index: index, - sort: - col.sort ?? typeof col.accessor === 'function' - ? undefined - : col.accessor, + sort: determineSortValue(col), isSorted: !col.isSorted ? true : col.isSortedDesc ? false : true, isSortedDesc: col.isSorted ? !col.isSortedDesc : col.isSortedDesc, }, diff --git a/libs/npm/core/src/components/table/utils/determineSort.ts b/libs/npm/core/src/components/table/utils/determineSort.ts new file mode 100644 index 0000000000..ce7af9c874 --- /dev/null +++ b/libs/npm/core/src/components/table/utils/determineSort.ts @@ -0,0 +1,16 @@ +import { ITableInternalHeaderColumn } from '../interfaces/ITableInternalHeaderColumn'; + +export const determineSortValue = (col: ITableInternalHeaderColumn) => { + // If 'col.sort' is defined and not null, use it directly for sorting + if (col.sort !== undefined && col.sort !== null) { + return col.sort; + } + // If 'col.sort' is undefined or null and 'accessor' is a function, + // it indicates that 'accessor' cannot be used for sorting directly (e.g., dynamic values) + if (typeof col.accessor === 'function') { + return undefined; + } + // If 'col.sort' is undefined or null, and 'accessor' is not a function, + // use 'accessor' as the field name for sorting + return col.accessor; +};