@tanstack/table-core 9.0.0-beta.5 → 9.0.0-beta.7

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.
@@ -60,19 +60,22 @@ function table_resetSorting(table, defaultState) {
60
60
  */
61
61
  function column_getAutoSortFn(column) {
62
62
  const sortFns = column.table._rowModelFns.sortFns;
63
- let sortFn;
64
63
  const firstRows = column.table.getFilteredRowModel().flatRows.slice(0, 10);
65
64
  let isString = false;
66
65
  for (let i = 0; i < firstRows.length; i++) {
67
66
  const value = firstRows[i].getValue(column.id);
68
- if (Object.prototype.toString.call(value) === "[object Date]") sortFn = sortFns === null || sortFns === void 0 ? void 0 : sortFns.datetime;
67
+ if (Object.prototype.toString.call(value) === "[object Date]") {
68
+ if (sortFns === null || sortFns === void 0 ? void 0 : sortFns.datetime) return sortFns.datetime;
69
+ }
69
70
  if (typeof value === "string") {
70
71
  isString = true;
71
- if (value.split(require_sortFns.reSplitAlphaNumeric).length > 1) sortFn = sortFns === null || sortFns === void 0 ? void 0 : sortFns.alphanumeric;
72
+ if (value.split(require_sortFns.reSplitAlphaNumeric).length > 1) {
73
+ if (sortFns === null || sortFns === void 0 ? void 0 : sortFns.alphanumeric) return sortFns.alphanumeric;
74
+ }
72
75
  }
73
76
  }
74
- if (isString) sortFn = sortFns === null || sortFns === void 0 ? void 0 : sortFns.text;
75
- return sortFn ?? require_sortFns.sortFn_basic;
77
+ if (isString) return (sortFns === null || sortFns === void 0 ? void 0 : sortFns.text) ?? require_sortFns.sortFn_basic;
78
+ return require_sortFns.sortFn_basic;
76
79
  }
77
80
  /**
78
81
  * Chooses the default first sort direction from the first filtered row value.
@@ -1 +1 @@
1
- {"version":3,"file":"rowSortingFeature.utils.cjs","names":["cloneState","reSplitAlphaNumeric","sortFn_basic","isFunction"],"sources":["../../../src/features/row-sorting/rowSortingFeature.utils.ts"],"sourcesContent":["import { reSplitAlphaNumeric, sortFn_basic } from '../../fns/sortFns'\nimport { cloneState, isFunction } from '../../utils'\nimport type { CellData, RowData, Updater } from '../../types/type-utils'\nimport type { TableFeatures } from '../../types/TableFeatures'\nimport type { Table_Internal } from '../../types/Table'\nimport type { Column_Internal } from '../../types/Column'\nimport type {\n SortDirection,\n SortFn,\n SortingState,\n} from './rowSortingFeature.types'\n\n// State Utils\n\n/**\n * Creates the default sorting state.\n *\n * The feature default is an empty array, meaning no columns are sorted. Reset\n * APIs use this value when `defaultState` is `true`.\n *\n * @example\n * ```ts\n * const sorting = getDefaultSortingState()\n * ```\n */\nexport function getDefaultSortingState(): SortingState {\n return []\n}\n\n/**\n * Routes a sorting updater through the table's sorting change handler.\n *\n * The updater may be a next `SortingState` array or a function of the previous\n * sorting state, matching the instance `table.setSorting` behavior.\n *\n * @example\n * ```ts\n * table_setSorting(table, (old) => [...old, { id: 'age', desc: true }])\n * ```\n */\nexport function table_setSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(table: Table_Internal<TFeatures, TData>, updater: Updater<SortingState>) {\n table.options.onSortingChange?.(updater)\n}\n\n/**\n * Resets `sorting` to the configured initial state or feature default.\n *\n * With no argument, the reset clones `table.initialState.sorting` when it\n * exists. Passing `true` ignores initial state and resets to `[]`.\n *\n * @example\n * ```ts\n * table_resetSorting(table)\n * table_resetSorting(table, true)\n * ```\n */\nexport function table_resetSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(table: Table_Internal<TFeatures, TData>, defaultState?: boolean) {\n table_setSorting(\n table,\n defaultState ? [] : cloneState(table.initialState.sorting ?? []),\n )\n}\n\n// Column Utils\n\n/**\n * Chooses a built-in sorting function from sampled filtered row values.\n *\n * Date-like values use `datetime`, mixed text/numeric strings use\n * `alphanumeric`, plain strings use `text`, and unknown values fall back to\n * `basic`.\n *\n * @example\n * ```ts\n * const sortFn = column_getAutoSortFn(column)\n * ```\n */\nexport function column_getAutoSortFn<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): SortFn<TFeatures, TData> {\n const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =\n column.table._rowModelFns.sortFns\n\n let sortFn: SortFn<TFeatures, TData> | undefined\n\n const firstRows = column.table.getFilteredRowModel().flatRows.slice(0, 10)\n\n let isString = false\n\n for (let i = 0; i < firstRows.length; i++) {\n const value = firstRows[i]!.getValue(column.id)\n\n if (Object.prototype.toString.call(value) === '[object Date]') {\n sortFn = sortFns?.datetime\n }\n\n if (typeof value === 'string') {\n isString = true\n\n if (value.split(reSplitAlphaNumeric).length > 1) {\n sortFn = sortFns?.alphanumeric\n }\n }\n }\n\n if (isString) {\n sortFn = sortFns?.text\n }\n\n return sortFn ?? sortFn_basic\n}\n\n/**\n * Chooses the default first sort direction from the first filtered row value.\n *\n * String columns start ascending so alphabetical order is natural; other value\n * types start descending.\n *\n * @example\n * ```ts\n * const direction = column_getAutoSortDir(column)\n * ```\n */\nexport function column_getAutoSortDir<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const firstRow = column.table.getFilteredRowModel().flatRows[0]\n\n const value = firstRow ? firstRow.getValue(column.id) : undefined\n\n if (typeof value === 'string') {\n return 'asc'\n }\n\n return 'desc'\n}\n\n/**\n * Resolves the sorting function configured for a column.\n *\n * Function-valued `columnDef.sortFn` is returned directly, `'auto'` delegates\n * to `column_getAutoSortFn`, and string values are looked up in the table's\n * sorting function registry before falling back to `basic`.\n *\n * @example\n * ```ts\n * const sortFn = column_getSortFn(column)\n * ```\n */\nexport function column_getSortFn<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): SortFn<TFeatures, TData> {\n const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =\n column.table._rowModelFns.sortFns\n\n return isFunction(column.columnDef.sortFn)\n ? column.columnDef.sortFn\n : column.columnDef.sortFn === 'auto'\n ? column_getAutoSortFn(column)\n : (sortFns?.[column.columnDef.sortFn as string] ?? sortFn_basic)\n}\n\n/**\n * Applies the next sorting state for this column.\n *\n * The toggle can add, replace, flip, or remove this column's sort entry. Multi\n * sorting respects `enableMultiSort`, `maxMultiSortColCount`, and the `multi`\n * argument.\n *\n * @example\n * ```ts\n * column_toggleSorting(column, undefined, true)\n * ```\n */\nexport function column_toggleSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(\n column: Column_Internal<TFeatures, TData, TValue>,\n desc?: boolean,\n multi?: boolean,\n) {\n // if (column.columns.length) {\n // column.columns.forEach((c, i) => {\n // if (c.id) {\n // table.toggleColumnSorting(c.id, undefined, multi || !!i)\n // }\n // })\n // return\n // }\n\n // this needs to be outside of table.setSorting to be in sync with rerender\n const nextSortingOrder = column_getNextSortingOrder(column)\n const hasManualValue = typeof desc !== 'undefined'\n\n table_setSorting(column.table, (old) => {\n // Find any existing sorting for this column\n const existingSorting = old.find((d) => d.id === column.id)\n const existingIndex = old.findIndex((d) => d.id === column.id)\n\n let newSorting: SortingState = []\n\n // What should we do with this sort action?\n let sortAction: 'add' | 'remove' | 'toggle' | 'replace'\n const nextDesc = hasManualValue ? desc : nextSortingOrder === 'desc'\n\n // Multi-mode\n if (old.length && column_getCanMultiSort(column) && multi) {\n if (existingSorting) {\n sortAction = 'toggle'\n } else {\n sortAction = 'add'\n }\n } else {\n // Normal mode\n if (old.length && existingIndex !== old.length - 1) {\n sortAction = 'replace'\n } else if (existingSorting) {\n sortAction = 'toggle'\n } else {\n sortAction = 'replace'\n }\n }\n\n // Handle toggle states that will remove the sorting\n if (sortAction === 'toggle') {\n // If we are \"actually\" toggling (not a manual set value), should we remove the sorting?\n if (!hasManualValue) {\n // Is our intention to remove?\n if (!nextSortingOrder) {\n sortAction = 'remove'\n }\n }\n }\n\n if (sortAction === 'add') {\n newSorting = [\n ...old,\n {\n id: column.id,\n desc: nextDesc,\n },\n ]\n // Take latest n columns\n newSorting.splice(\n 0,\n newSorting.length -\n (column.table.options.maxMultiSortColCount ??\n Number.MAX_SAFE_INTEGER),\n )\n } else if (sortAction === 'toggle') {\n // This flips (or sets) the\n newSorting = old.map((d) => {\n if (d.id === column.id) {\n return {\n ...d,\n desc: nextDesc,\n }\n }\n return d\n })\n } else if (sortAction === 'remove') {\n newSorting = old.filter((d) => d.id !== column.id)\n } else {\n newSorting = [\n {\n id: column.id,\n desc: nextDesc,\n },\n ]\n }\n\n return newSorting\n })\n}\n\n/**\n * Resolves the first direction used when this column begins sorting.\n *\n * Column-level `sortDescFirst` wins, then table-level `sortDescFirst`, then the\n * auto direction inferred from sampled values.\n *\n * @example\n * ```ts\n * const firstDirection = column_getFirstSortDir(column)\n * ```\n */\nexport function column_getFirstSortDir<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const sortDescFirst =\n column.columnDef.sortDescFirst ??\n column.table.options.sortDescFirst ??\n column_getAutoSortDir(column) === 'desc'\n return sortDescFirst ? 'desc' : 'asc'\n}\n\n/**\n * Resolves the next sort order for this column's toggle cycle.\n *\n * The cycle starts with the first sort direction, flips between `asc` and\n * `desc`, and can return `false` when sorting removal is enabled.\n *\n * @example\n * ```ts\n * const nextOrder = column_getNextSortingOrder(column)\n * ```\n */\nexport function column_getNextSortingOrder<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>, multi?: boolean) {\n const firstSortDirection = column_getFirstSortDir(column)\n const isSorted = column_getIsSorted(column)\n\n if (!isSorted) {\n return firstSortDirection\n }\n\n if (\n isSorted !== firstSortDirection &&\n (column.table.options.enableSortingRemoval ?? true) && // If enableSortRemove, enable in general\n (multi ? (column.table.options.enableMultiRemove ?? true) : true) // If multi, don't allow if enableMultiRemove))\n ) {\n return false\n }\n return isSorted === 'desc' ? 'asc' : 'desc'\n}\n\n/**\n * Checks whether this accessor column can participate in sorting.\n *\n * The column must have an accessor and sorting must be enabled by both the\n * column definition and table options.\n *\n * @example\n * ```ts\n * const canSort = column_getCanSort(column)\n * ```\n */\nexport function column_getCanSort<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n return (\n (column.columnDef.enableSorting ?? true) &&\n (column.table.options.enableSorting ?? true) &&\n !!column.accessorFn\n )\n}\n\n/**\n * Checks whether this column can be added to a multi-sort state.\n *\n * Column-level `enableMultiSort` wins over table-level `enableMultiSort`; if\n * neither is set, accessor columns can multi-sort by default.\n *\n * @example\n * ```ts\n * const canMultiSort = column_getCanMultiSort(column)\n * ```\n */\nexport function column_getCanMultiSort<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): boolean {\n return (\n column.columnDef.enableMultiSort ??\n column.table.options.enableMultiSort ??\n !!column.accessorFn\n )\n}\n\n/**\n * Reads this column's current sort direction.\n *\n * The result is `false` when the column is not sorted, otherwise `'asc'` or\n * `'desc'` based on the column's entry in `state.sorting`.\n *\n * @example\n * ```ts\n * const direction = column_getIsSorted(column)\n * ```\n */\nexport function column_getIsSorted<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): false | SortDirection {\n const columnSort = column.table.atoms.sorting\n ?.get()\n ?.find((d) => d.id === column.id)\n return !columnSort ? false : columnSort.desc ? 'desc' : 'asc'\n}\n\n/**\n * Finds this column's position in the ordered `state.sorting` array.\n *\n * The result is `-1` when the column is not sorted.\n *\n * @example\n * ```ts\n * const index = column_getSortIndex(column)\n * ```\n */\nexport function column_getSortIndex<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): number {\n return (\n column.table.atoms.sorting?.get()?.findIndex((d) => d.id === column.id) ??\n -1\n )\n}\n\n/**\n * Removes this column from the sorting state.\n *\n * Other sorted columns are preserved, including their relative order.\n *\n * @example\n * ```ts\n * column_clearSorting(column)\n * ```\n */\nexport function column_clearSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n // clear sorting for just 1 column\n table_setSorting(column.table, (old) =>\n old.length ? old.filter((d) => d.id !== column.id) : [],\n )\n}\n\n/**\n * Creates a header event handler that toggles this column's sorting.\n *\n * The handler ignores events when the column cannot sort, persists React-style\n * synthetic events when present, and asks `options.isMultiSortEvent` whether\n * the event should add to a multi-sort.\n *\n * @example\n * ```ts\n * const onClick = column_getToggleSortingHandler(column)\n * ```\n */\nexport function column_getToggleSortingHandler<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const canSort = column_getCanSort(column)\n\n return (e: unknown) => {\n if (!canSort) return\n ;(e as any).persist?.()\n column_toggleSorting(\n column,\n\n undefined,\n column_getCanMultiSort(column)\n ? column.table.options.isMultiSortEvent?.(e)\n : false,\n )\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rowSortingFeature.utils.cjs","names":["cloneState","reSplitAlphaNumeric","sortFn_basic","isFunction"],"sources":["../../../src/features/row-sorting/rowSortingFeature.utils.ts"],"sourcesContent":["import { reSplitAlphaNumeric, sortFn_basic } from '../../fns/sortFns'\nimport { cloneState, isFunction } from '../../utils'\nimport type { CellData, RowData, Updater } from '../../types/type-utils'\nimport type { TableFeatures } from '../../types/TableFeatures'\nimport type { Table_Internal } from '../../types/Table'\nimport type { Column_Internal } from '../../types/Column'\nimport type {\n SortDirection,\n SortFn,\n SortingState,\n} from './rowSortingFeature.types'\n\n// State Utils\n\n/**\n * Creates the default sorting state.\n *\n * The feature default is an empty array, meaning no columns are sorted. Reset\n * APIs use this value when `defaultState` is `true`.\n *\n * @example\n * ```ts\n * const sorting = getDefaultSortingState()\n * ```\n */\nexport function getDefaultSortingState(): SortingState {\n return []\n}\n\n/**\n * Routes a sorting updater through the table's sorting change handler.\n *\n * The updater may be a next `SortingState` array or a function of the previous\n * sorting state, matching the instance `table.setSorting` behavior.\n *\n * @example\n * ```ts\n * table_setSorting(table, (old) => [...old, { id: 'age', desc: true }])\n * ```\n */\nexport function table_setSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(table: Table_Internal<TFeatures, TData>, updater: Updater<SortingState>) {\n table.options.onSortingChange?.(updater)\n}\n\n/**\n * Resets `sorting` to the configured initial state or feature default.\n *\n * With no argument, the reset clones `table.initialState.sorting` when it\n * exists. Passing `true` ignores initial state and resets to `[]`.\n *\n * @example\n * ```ts\n * table_resetSorting(table)\n * table_resetSorting(table, true)\n * ```\n */\nexport function table_resetSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(table: Table_Internal<TFeatures, TData>, defaultState?: boolean) {\n table_setSorting(\n table,\n defaultState ? [] : cloneState(table.initialState.sorting ?? []),\n )\n}\n\n// Column Utils\n\n/**\n * Chooses a built-in sorting function from sampled filtered row values.\n *\n * Date-like values use `datetime`, mixed text/numeric strings use\n * `alphanumeric`, plain strings use `text`, and unknown values fall back to\n * `basic`.\n *\n * @example\n * ```ts\n * const sortFn = column_getAutoSortFn(column)\n * ```\n */\nexport function column_getAutoSortFn<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): SortFn<TFeatures, TData> {\n const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =\n column.table._rowModelFns.sortFns\n\n const firstRows = column.table.getFilteredRowModel().flatRows.slice(0, 10)\n\n let isString = false\n\n for (let i = 0; i < firstRows.length; i++) {\n const value = firstRows[i]!.getValue(column.id)\n\n if (Object.prototype.toString.call(value) === '[object Date]') {\n if (sortFns?.datetime) {\n return sortFns.datetime\n }\n }\n\n if (typeof value === 'string') {\n isString = true\n\n if (value.split(reSplitAlphaNumeric).length > 1) {\n if (sortFns?.alphanumeric) {\n return sortFns.alphanumeric\n }\n }\n }\n }\n\n if (isString) {\n return sortFns?.text ?? sortFn_basic\n }\n\n return sortFn_basic\n}\n\n/**\n * Chooses the default first sort direction from the first filtered row value.\n *\n * String columns start ascending so alphabetical order is natural; other value\n * types start descending.\n *\n * @example\n * ```ts\n * const direction = column_getAutoSortDir(column)\n * ```\n */\nexport function column_getAutoSortDir<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const firstRow = column.table.getFilteredRowModel().flatRows[0]\n\n const value = firstRow ? firstRow.getValue(column.id) : undefined\n\n if (typeof value === 'string') {\n return 'asc'\n }\n\n return 'desc'\n}\n\n/**\n * Resolves the sorting function configured for a column.\n *\n * Function-valued `columnDef.sortFn` is returned directly, `'auto'` delegates\n * to `column_getAutoSortFn`, and string values are looked up in the table's\n * sorting function registry before falling back to `basic`.\n *\n * @example\n * ```ts\n * const sortFn = column_getSortFn(column)\n * ```\n */\nexport function column_getSortFn<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): SortFn<TFeatures, TData> {\n const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =\n column.table._rowModelFns.sortFns\n\n return isFunction(column.columnDef.sortFn)\n ? column.columnDef.sortFn\n : column.columnDef.sortFn === 'auto'\n ? column_getAutoSortFn(column)\n : (sortFns?.[column.columnDef.sortFn as string] ?? sortFn_basic)\n}\n\n/**\n * Applies the next sorting state for this column.\n *\n * The toggle can add, replace, flip, or remove this column's sort entry. Multi\n * sorting respects `enableMultiSort`, `maxMultiSortColCount`, and the `multi`\n * argument.\n *\n * @example\n * ```ts\n * column_toggleSorting(column, undefined, true)\n * ```\n */\nexport function column_toggleSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(\n column: Column_Internal<TFeatures, TData, TValue>,\n desc?: boolean,\n multi?: boolean,\n) {\n // if (column.columns.length) {\n // column.columns.forEach((c, i) => {\n // if (c.id) {\n // table.toggleColumnSorting(c.id, undefined, multi || !!i)\n // }\n // })\n // return\n // }\n\n // this needs to be outside of table.setSorting to be in sync with rerender\n const nextSortingOrder = column_getNextSortingOrder(column)\n const hasManualValue = typeof desc !== 'undefined'\n\n table_setSorting(column.table, (old) => {\n // Find any existing sorting for this column\n const existingSorting = old.find((d) => d.id === column.id)\n const existingIndex = old.findIndex((d) => d.id === column.id)\n\n let newSorting: SortingState = []\n\n // What should we do with this sort action?\n let sortAction: 'add' | 'remove' | 'toggle' | 'replace'\n const nextDesc = hasManualValue ? desc : nextSortingOrder === 'desc'\n\n // Multi-mode\n if (old.length && column_getCanMultiSort(column) && multi) {\n if (existingSorting) {\n sortAction = 'toggle'\n } else {\n sortAction = 'add'\n }\n } else {\n // Normal mode\n if (old.length && existingIndex !== old.length - 1) {\n sortAction = 'replace'\n } else if (existingSorting) {\n sortAction = 'toggle'\n } else {\n sortAction = 'replace'\n }\n }\n\n // Handle toggle states that will remove the sorting\n if (sortAction === 'toggle') {\n // If we are \"actually\" toggling (not a manual set value), should we remove the sorting?\n if (!hasManualValue) {\n // Is our intention to remove?\n if (!nextSortingOrder) {\n sortAction = 'remove'\n }\n }\n }\n\n if (sortAction === 'add') {\n newSorting = [\n ...old,\n {\n id: column.id,\n desc: nextDesc,\n },\n ]\n // Take latest n columns\n newSorting.splice(\n 0,\n newSorting.length -\n (column.table.options.maxMultiSortColCount ??\n Number.MAX_SAFE_INTEGER),\n )\n } else if (sortAction === 'toggle') {\n // This flips (or sets) the\n newSorting = old.map((d) => {\n if (d.id === column.id) {\n return {\n ...d,\n desc: nextDesc,\n }\n }\n return d\n })\n } else if (sortAction === 'remove') {\n newSorting = old.filter((d) => d.id !== column.id)\n } else {\n newSorting = [\n {\n id: column.id,\n desc: nextDesc,\n },\n ]\n }\n\n return newSorting\n })\n}\n\n/**\n * Resolves the first direction used when this column begins sorting.\n *\n * Column-level `sortDescFirst` wins, then table-level `sortDescFirst`, then the\n * auto direction inferred from sampled values.\n *\n * @example\n * ```ts\n * const firstDirection = column_getFirstSortDir(column)\n * ```\n */\nexport function column_getFirstSortDir<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const sortDescFirst =\n column.columnDef.sortDescFirst ??\n column.table.options.sortDescFirst ??\n column_getAutoSortDir(column) === 'desc'\n return sortDescFirst ? 'desc' : 'asc'\n}\n\n/**\n * Resolves the next sort order for this column's toggle cycle.\n *\n * The cycle starts with the first sort direction, flips between `asc` and\n * `desc`, and can return `false` when sorting removal is enabled.\n *\n * @example\n * ```ts\n * const nextOrder = column_getNextSortingOrder(column)\n * ```\n */\nexport function column_getNextSortingOrder<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>, multi?: boolean) {\n const firstSortDirection = column_getFirstSortDir(column)\n const isSorted = column_getIsSorted(column)\n\n if (!isSorted) {\n return firstSortDirection\n }\n\n if (\n isSorted !== firstSortDirection &&\n (column.table.options.enableSortingRemoval ?? true) && // If enableSortRemove, enable in general\n (multi ? (column.table.options.enableMultiRemove ?? true) : true) // If multi, don't allow if enableMultiRemove))\n ) {\n return false\n }\n return isSorted === 'desc' ? 'asc' : 'desc'\n}\n\n/**\n * Checks whether this accessor column can participate in sorting.\n *\n * The column must have an accessor and sorting must be enabled by both the\n * column definition and table options.\n *\n * @example\n * ```ts\n * const canSort = column_getCanSort(column)\n * ```\n */\nexport function column_getCanSort<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n return (\n (column.columnDef.enableSorting ?? true) &&\n (column.table.options.enableSorting ?? true) &&\n !!column.accessorFn\n )\n}\n\n/**\n * Checks whether this column can be added to a multi-sort state.\n *\n * Column-level `enableMultiSort` wins over table-level `enableMultiSort`; if\n * neither is set, accessor columns can multi-sort by default.\n *\n * @example\n * ```ts\n * const canMultiSort = column_getCanMultiSort(column)\n * ```\n */\nexport function column_getCanMultiSort<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): boolean {\n return (\n column.columnDef.enableMultiSort ??\n column.table.options.enableMultiSort ??\n !!column.accessorFn\n )\n}\n\n/**\n * Reads this column's current sort direction.\n *\n * The result is `false` when the column is not sorted, otherwise `'asc'` or\n * `'desc'` based on the column's entry in `state.sorting`.\n *\n * @example\n * ```ts\n * const direction = column_getIsSorted(column)\n * ```\n */\nexport function column_getIsSorted<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): false | SortDirection {\n const columnSort = column.table.atoms.sorting\n ?.get()\n ?.find((d) => d.id === column.id)\n return !columnSort ? false : columnSort.desc ? 'desc' : 'asc'\n}\n\n/**\n * Finds this column's position in the ordered `state.sorting` array.\n *\n * The result is `-1` when the column is not sorted.\n *\n * @example\n * ```ts\n * const index = column_getSortIndex(column)\n * ```\n */\nexport function column_getSortIndex<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): number {\n return (\n column.table.atoms.sorting?.get()?.findIndex((d) => d.id === column.id) ??\n -1\n )\n}\n\n/**\n * Removes this column from the sorting state.\n *\n * Other sorted columns are preserved, including their relative order.\n *\n * @example\n * ```ts\n * column_clearSorting(column)\n * ```\n */\nexport function column_clearSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n // clear sorting for just 1 column\n table_setSorting(column.table, (old) =>\n old.length ? old.filter((d) => d.id !== column.id) : [],\n )\n}\n\n/**\n * Creates a header event handler that toggles this column's sorting.\n *\n * The handler ignores events when the column cannot sort, persists React-style\n * synthetic events when present, and asks `options.isMultiSortEvent` whether\n * the event should add to a multi-sort.\n *\n * @example\n * ```ts\n * const onClick = column_getToggleSortingHandler(column)\n * ```\n */\nexport function column_getToggleSortingHandler<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const canSort = column_getCanSort(column)\n\n return (e: unknown) => {\n if (!canSort) return\n ;(e as any).persist?.()\n column_toggleSorting(\n column,\n\n undefined,\n column_getCanMultiSort(column)\n ? column.table.options.isMultiSortEvent?.(e)\n : false,\n )\n }\n}\n"],"mappings":"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"}
@@ -60,19 +60,22 @@ function table_resetSorting(table, defaultState) {
60
60
  */
61
61
  function column_getAutoSortFn(column) {
62
62
  const sortFns = column.table._rowModelFns.sortFns;
63
- let sortFn;
64
63
  const firstRows = column.table.getFilteredRowModel().flatRows.slice(0, 10);
65
64
  let isString = false;
66
65
  for (let i = 0; i < firstRows.length; i++) {
67
66
  const value = firstRows[i].getValue(column.id);
68
- if (Object.prototype.toString.call(value) === "[object Date]") sortFn = sortFns === null || sortFns === void 0 ? void 0 : sortFns.datetime;
67
+ if (Object.prototype.toString.call(value) === "[object Date]") {
68
+ if (sortFns === null || sortFns === void 0 ? void 0 : sortFns.datetime) return sortFns.datetime;
69
+ }
69
70
  if (typeof value === "string") {
70
71
  isString = true;
71
- if (value.split(reSplitAlphaNumeric).length > 1) sortFn = sortFns === null || sortFns === void 0 ? void 0 : sortFns.alphanumeric;
72
+ if (value.split(reSplitAlphaNumeric).length > 1) {
73
+ if (sortFns === null || sortFns === void 0 ? void 0 : sortFns.alphanumeric) return sortFns.alphanumeric;
74
+ }
72
75
  }
73
76
  }
74
- if (isString) sortFn = sortFns === null || sortFns === void 0 ? void 0 : sortFns.text;
75
- return sortFn ?? sortFn_basic;
77
+ if (isString) return (sortFns === null || sortFns === void 0 ? void 0 : sortFns.text) ?? sortFn_basic;
78
+ return sortFn_basic;
76
79
  }
77
80
  /**
78
81
  * Chooses the default first sort direction from the first filtered row value.
@@ -1 +1 @@
1
- {"version":3,"file":"rowSortingFeature.utils.js","names":[],"sources":["../../../src/features/row-sorting/rowSortingFeature.utils.ts"],"sourcesContent":["import { reSplitAlphaNumeric, sortFn_basic } from '../../fns/sortFns'\nimport { cloneState, isFunction } from '../../utils'\nimport type { CellData, RowData, Updater } from '../../types/type-utils'\nimport type { TableFeatures } from '../../types/TableFeatures'\nimport type { Table_Internal } from '../../types/Table'\nimport type { Column_Internal } from '../../types/Column'\nimport type {\n SortDirection,\n SortFn,\n SortingState,\n} from './rowSortingFeature.types'\n\n// State Utils\n\n/**\n * Creates the default sorting state.\n *\n * The feature default is an empty array, meaning no columns are sorted. Reset\n * APIs use this value when `defaultState` is `true`.\n *\n * @example\n * ```ts\n * const sorting = getDefaultSortingState()\n * ```\n */\nexport function getDefaultSortingState(): SortingState {\n return []\n}\n\n/**\n * Routes a sorting updater through the table's sorting change handler.\n *\n * The updater may be a next `SortingState` array or a function of the previous\n * sorting state, matching the instance `table.setSorting` behavior.\n *\n * @example\n * ```ts\n * table_setSorting(table, (old) => [...old, { id: 'age', desc: true }])\n * ```\n */\nexport function table_setSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(table: Table_Internal<TFeatures, TData>, updater: Updater<SortingState>) {\n table.options.onSortingChange?.(updater)\n}\n\n/**\n * Resets `sorting` to the configured initial state or feature default.\n *\n * With no argument, the reset clones `table.initialState.sorting` when it\n * exists. Passing `true` ignores initial state and resets to `[]`.\n *\n * @example\n * ```ts\n * table_resetSorting(table)\n * table_resetSorting(table, true)\n * ```\n */\nexport function table_resetSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(table: Table_Internal<TFeatures, TData>, defaultState?: boolean) {\n table_setSorting(\n table,\n defaultState ? [] : cloneState(table.initialState.sorting ?? []),\n )\n}\n\n// Column Utils\n\n/**\n * Chooses a built-in sorting function from sampled filtered row values.\n *\n * Date-like values use `datetime`, mixed text/numeric strings use\n * `alphanumeric`, plain strings use `text`, and unknown values fall back to\n * `basic`.\n *\n * @example\n * ```ts\n * const sortFn = column_getAutoSortFn(column)\n * ```\n */\nexport function column_getAutoSortFn<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): SortFn<TFeatures, TData> {\n const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =\n column.table._rowModelFns.sortFns\n\n let sortFn: SortFn<TFeatures, TData> | undefined\n\n const firstRows = column.table.getFilteredRowModel().flatRows.slice(0, 10)\n\n let isString = false\n\n for (let i = 0; i < firstRows.length; i++) {\n const value = firstRows[i]!.getValue(column.id)\n\n if (Object.prototype.toString.call(value) === '[object Date]') {\n sortFn = sortFns?.datetime\n }\n\n if (typeof value === 'string') {\n isString = true\n\n if (value.split(reSplitAlphaNumeric).length > 1) {\n sortFn = sortFns?.alphanumeric\n }\n }\n }\n\n if (isString) {\n sortFn = sortFns?.text\n }\n\n return sortFn ?? sortFn_basic\n}\n\n/**\n * Chooses the default first sort direction from the first filtered row value.\n *\n * String columns start ascending so alphabetical order is natural; other value\n * types start descending.\n *\n * @example\n * ```ts\n * const direction = column_getAutoSortDir(column)\n * ```\n */\nexport function column_getAutoSortDir<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const firstRow = column.table.getFilteredRowModel().flatRows[0]\n\n const value = firstRow ? firstRow.getValue(column.id) : undefined\n\n if (typeof value === 'string') {\n return 'asc'\n }\n\n return 'desc'\n}\n\n/**\n * Resolves the sorting function configured for a column.\n *\n * Function-valued `columnDef.sortFn` is returned directly, `'auto'` delegates\n * to `column_getAutoSortFn`, and string values are looked up in the table's\n * sorting function registry before falling back to `basic`.\n *\n * @example\n * ```ts\n * const sortFn = column_getSortFn(column)\n * ```\n */\nexport function column_getSortFn<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): SortFn<TFeatures, TData> {\n const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =\n column.table._rowModelFns.sortFns\n\n return isFunction(column.columnDef.sortFn)\n ? column.columnDef.sortFn\n : column.columnDef.sortFn === 'auto'\n ? column_getAutoSortFn(column)\n : (sortFns?.[column.columnDef.sortFn as string] ?? sortFn_basic)\n}\n\n/**\n * Applies the next sorting state for this column.\n *\n * The toggle can add, replace, flip, or remove this column's sort entry. Multi\n * sorting respects `enableMultiSort`, `maxMultiSortColCount`, and the `multi`\n * argument.\n *\n * @example\n * ```ts\n * column_toggleSorting(column, undefined, true)\n * ```\n */\nexport function column_toggleSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(\n column: Column_Internal<TFeatures, TData, TValue>,\n desc?: boolean,\n multi?: boolean,\n) {\n // if (column.columns.length) {\n // column.columns.forEach((c, i) => {\n // if (c.id) {\n // table.toggleColumnSorting(c.id, undefined, multi || !!i)\n // }\n // })\n // return\n // }\n\n // this needs to be outside of table.setSorting to be in sync with rerender\n const nextSortingOrder = column_getNextSortingOrder(column)\n const hasManualValue = typeof desc !== 'undefined'\n\n table_setSorting(column.table, (old) => {\n // Find any existing sorting for this column\n const existingSorting = old.find((d) => d.id === column.id)\n const existingIndex = old.findIndex((d) => d.id === column.id)\n\n let newSorting: SortingState = []\n\n // What should we do with this sort action?\n let sortAction: 'add' | 'remove' | 'toggle' | 'replace'\n const nextDesc = hasManualValue ? desc : nextSortingOrder === 'desc'\n\n // Multi-mode\n if (old.length && column_getCanMultiSort(column) && multi) {\n if (existingSorting) {\n sortAction = 'toggle'\n } else {\n sortAction = 'add'\n }\n } else {\n // Normal mode\n if (old.length && existingIndex !== old.length - 1) {\n sortAction = 'replace'\n } else if (existingSorting) {\n sortAction = 'toggle'\n } else {\n sortAction = 'replace'\n }\n }\n\n // Handle toggle states that will remove the sorting\n if (sortAction === 'toggle') {\n // If we are \"actually\" toggling (not a manual set value), should we remove the sorting?\n if (!hasManualValue) {\n // Is our intention to remove?\n if (!nextSortingOrder) {\n sortAction = 'remove'\n }\n }\n }\n\n if (sortAction === 'add') {\n newSorting = [\n ...old,\n {\n id: column.id,\n desc: nextDesc,\n },\n ]\n // Take latest n columns\n newSorting.splice(\n 0,\n newSorting.length -\n (column.table.options.maxMultiSortColCount ??\n Number.MAX_SAFE_INTEGER),\n )\n } else if (sortAction === 'toggle') {\n // This flips (or sets) the\n newSorting = old.map((d) => {\n if (d.id === column.id) {\n return {\n ...d,\n desc: nextDesc,\n }\n }\n return d\n })\n } else if (sortAction === 'remove') {\n newSorting = old.filter((d) => d.id !== column.id)\n } else {\n newSorting = [\n {\n id: column.id,\n desc: nextDesc,\n },\n ]\n }\n\n return newSorting\n })\n}\n\n/**\n * Resolves the first direction used when this column begins sorting.\n *\n * Column-level `sortDescFirst` wins, then table-level `sortDescFirst`, then the\n * auto direction inferred from sampled values.\n *\n * @example\n * ```ts\n * const firstDirection = column_getFirstSortDir(column)\n * ```\n */\nexport function column_getFirstSortDir<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const sortDescFirst =\n column.columnDef.sortDescFirst ??\n column.table.options.sortDescFirst ??\n column_getAutoSortDir(column) === 'desc'\n return sortDescFirst ? 'desc' : 'asc'\n}\n\n/**\n * Resolves the next sort order for this column's toggle cycle.\n *\n * The cycle starts with the first sort direction, flips between `asc` and\n * `desc`, and can return `false` when sorting removal is enabled.\n *\n * @example\n * ```ts\n * const nextOrder = column_getNextSortingOrder(column)\n * ```\n */\nexport function column_getNextSortingOrder<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>, multi?: boolean) {\n const firstSortDirection = column_getFirstSortDir(column)\n const isSorted = column_getIsSorted(column)\n\n if (!isSorted) {\n return firstSortDirection\n }\n\n if (\n isSorted !== firstSortDirection &&\n (column.table.options.enableSortingRemoval ?? true) && // If enableSortRemove, enable in general\n (multi ? (column.table.options.enableMultiRemove ?? true) : true) // If multi, don't allow if enableMultiRemove))\n ) {\n return false\n }\n return isSorted === 'desc' ? 'asc' : 'desc'\n}\n\n/**\n * Checks whether this accessor column can participate in sorting.\n *\n * The column must have an accessor and sorting must be enabled by both the\n * column definition and table options.\n *\n * @example\n * ```ts\n * const canSort = column_getCanSort(column)\n * ```\n */\nexport function column_getCanSort<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n return (\n (column.columnDef.enableSorting ?? true) &&\n (column.table.options.enableSorting ?? true) &&\n !!column.accessorFn\n )\n}\n\n/**\n * Checks whether this column can be added to a multi-sort state.\n *\n * Column-level `enableMultiSort` wins over table-level `enableMultiSort`; if\n * neither is set, accessor columns can multi-sort by default.\n *\n * @example\n * ```ts\n * const canMultiSort = column_getCanMultiSort(column)\n * ```\n */\nexport function column_getCanMultiSort<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): boolean {\n return (\n column.columnDef.enableMultiSort ??\n column.table.options.enableMultiSort ??\n !!column.accessorFn\n )\n}\n\n/**\n * Reads this column's current sort direction.\n *\n * The result is `false` when the column is not sorted, otherwise `'asc'` or\n * `'desc'` based on the column's entry in `state.sorting`.\n *\n * @example\n * ```ts\n * const direction = column_getIsSorted(column)\n * ```\n */\nexport function column_getIsSorted<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): false | SortDirection {\n const columnSort = column.table.atoms.sorting\n ?.get()\n ?.find((d) => d.id === column.id)\n return !columnSort ? false : columnSort.desc ? 'desc' : 'asc'\n}\n\n/**\n * Finds this column's position in the ordered `state.sorting` array.\n *\n * The result is `-1` when the column is not sorted.\n *\n * @example\n * ```ts\n * const index = column_getSortIndex(column)\n * ```\n */\nexport function column_getSortIndex<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): number {\n return (\n column.table.atoms.sorting?.get()?.findIndex((d) => d.id === column.id) ??\n -1\n )\n}\n\n/**\n * Removes this column from the sorting state.\n *\n * Other sorted columns are preserved, including their relative order.\n *\n * @example\n * ```ts\n * column_clearSorting(column)\n * ```\n */\nexport function column_clearSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n // clear sorting for just 1 column\n table_setSorting(column.table, (old) =>\n old.length ? old.filter((d) => d.id !== column.id) : [],\n )\n}\n\n/**\n * Creates a header event handler that toggles this column's sorting.\n *\n * The handler ignores events when the column cannot sort, persists React-style\n * synthetic events when present, and asks `options.isMultiSortEvent` whether\n * the event should add to a multi-sort.\n *\n * @example\n * ```ts\n * const onClick = column_getToggleSortingHandler(column)\n * ```\n */\nexport function column_getToggleSortingHandler<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const canSort = column_getCanSort(column)\n\n return (e: unknown) => {\n if (!canSort) return\n ;(e as any).persist?.()\n column_toggleSorting(\n column,\n\n undefined,\n column_getCanMultiSort(column)\n ? column.table.options.isMultiSortEvent?.(e)\n : false,\n )\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rowSortingFeature.utils.js","names":[],"sources":["../../../src/features/row-sorting/rowSortingFeature.utils.ts"],"sourcesContent":["import { reSplitAlphaNumeric, sortFn_basic } from '../../fns/sortFns'\nimport { cloneState, isFunction } from '../../utils'\nimport type { CellData, RowData, Updater } from '../../types/type-utils'\nimport type { TableFeatures } from '../../types/TableFeatures'\nimport type { Table_Internal } from '../../types/Table'\nimport type { Column_Internal } from '../../types/Column'\nimport type {\n SortDirection,\n SortFn,\n SortingState,\n} from './rowSortingFeature.types'\n\n// State Utils\n\n/**\n * Creates the default sorting state.\n *\n * The feature default is an empty array, meaning no columns are sorted. Reset\n * APIs use this value when `defaultState` is `true`.\n *\n * @example\n * ```ts\n * const sorting = getDefaultSortingState()\n * ```\n */\nexport function getDefaultSortingState(): SortingState {\n return []\n}\n\n/**\n * Routes a sorting updater through the table's sorting change handler.\n *\n * The updater may be a next `SortingState` array or a function of the previous\n * sorting state, matching the instance `table.setSorting` behavior.\n *\n * @example\n * ```ts\n * table_setSorting(table, (old) => [...old, { id: 'age', desc: true }])\n * ```\n */\nexport function table_setSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(table: Table_Internal<TFeatures, TData>, updater: Updater<SortingState>) {\n table.options.onSortingChange?.(updater)\n}\n\n/**\n * Resets `sorting` to the configured initial state or feature default.\n *\n * With no argument, the reset clones `table.initialState.sorting` when it\n * exists. Passing `true` ignores initial state and resets to `[]`.\n *\n * @example\n * ```ts\n * table_resetSorting(table)\n * table_resetSorting(table, true)\n * ```\n */\nexport function table_resetSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(table: Table_Internal<TFeatures, TData>, defaultState?: boolean) {\n table_setSorting(\n table,\n defaultState ? [] : cloneState(table.initialState.sorting ?? []),\n )\n}\n\n// Column Utils\n\n/**\n * Chooses a built-in sorting function from sampled filtered row values.\n *\n * Date-like values use `datetime`, mixed text/numeric strings use\n * `alphanumeric`, plain strings use `text`, and unknown values fall back to\n * `basic`.\n *\n * @example\n * ```ts\n * const sortFn = column_getAutoSortFn(column)\n * ```\n */\nexport function column_getAutoSortFn<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): SortFn<TFeatures, TData> {\n const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =\n column.table._rowModelFns.sortFns\n\n const firstRows = column.table.getFilteredRowModel().flatRows.slice(0, 10)\n\n let isString = false\n\n for (let i = 0; i < firstRows.length; i++) {\n const value = firstRows[i]!.getValue(column.id)\n\n if (Object.prototype.toString.call(value) === '[object Date]') {\n if (sortFns?.datetime) {\n return sortFns.datetime\n }\n }\n\n if (typeof value === 'string') {\n isString = true\n\n if (value.split(reSplitAlphaNumeric).length > 1) {\n if (sortFns?.alphanumeric) {\n return sortFns.alphanumeric\n }\n }\n }\n }\n\n if (isString) {\n return sortFns?.text ?? sortFn_basic\n }\n\n return sortFn_basic\n}\n\n/**\n * Chooses the default first sort direction from the first filtered row value.\n *\n * String columns start ascending so alphabetical order is natural; other value\n * types start descending.\n *\n * @example\n * ```ts\n * const direction = column_getAutoSortDir(column)\n * ```\n */\nexport function column_getAutoSortDir<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const firstRow = column.table.getFilteredRowModel().flatRows[0]\n\n const value = firstRow ? firstRow.getValue(column.id) : undefined\n\n if (typeof value === 'string') {\n return 'asc'\n }\n\n return 'desc'\n}\n\n/**\n * Resolves the sorting function configured for a column.\n *\n * Function-valued `columnDef.sortFn` is returned directly, `'auto'` delegates\n * to `column_getAutoSortFn`, and string values are looked up in the table's\n * sorting function registry before falling back to `basic`.\n *\n * @example\n * ```ts\n * const sortFn = column_getSortFn(column)\n * ```\n */\nexport function column_getSortFn<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): SortFn<TFeatures, TData> {\n const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =\n column.table._rowModelFns.sortFns\n\n return isFunction(column.columnDef.sortFn)\n ? column.columnDef.sortFn\n : column.columnDef.sortFn === 'auto'\n ? column_getAutoSortFn(column)\n : (sortFns?.[column.columnDef.sortFn as string] ?? sortFn_basic)\n}\n\n/**\n * Applies the next sorting state for this column.\n *\n * The toggle can add, replace, flip, or remove this column's sort entry. Multi\n * sorting respects `enableMultiSort`, `maxMultiSortColCount`, and the `multi`\n * argument.\n *\n * @example\n * ```ts\n * column_toggleSorting(column, undefined, true)\n * ```\n */\nexport function column_toggleSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(\n column: Column_Internal<TFeatures, TData, TValue>,\n desc?: boolean,\n multi?: boolean,\n) {\n // if (column.columns.length) {\n // column.columns.forEach((c, i) => {\n // if (c.id) {\n // table.toggleColumnSorting(c.id, undefined, multi || !!i)\n // }\n // })\n // return\n // }\n\n // this needs to be outside of table.setSorting to be in sync with rerender\n const nextSortingOrder = column_getNextSortingOrder(column)\n const hasManualValue = typeof desc !== 'undefined'\n\n table_setSorting(column.table, (old) => {\n // Find any existing sorting for this column\n const existingSorting = old.find((d) => d.id === column.id)\n const existingIndex = old.findIndex((d) => d.id === column.id)\n\n let newSorting: SortingState = []\n\n // What should we do with this sort action?\n let sortAction: 'add' | 'remove' | 'toggle' | 'replace'\n const nextDesc = hasManualValue ? desc : nextSortingOrder === 'desc'\n\n // Multi-mode\n if (old.length && column_getCanMultiSort(column) && multi) {\n if (existingSorting) {\n sortAction = 'toggle'\n } else {\n sortAction = 'add'\n }\n } else {\n // Normal mode\n if (old.length && existingIndex !== old.length - 1) {\n sortAction = 'replace'\n } else if (existingSorting) {\n sortAction = 'toggle'\n } else {\n sortAction = 'replace'\n }\n }\n\n // Handle toggle states that will remove the sorting\n if (sortAction === 'toggle') {\n // If we are \"actually\" toggling (not a manual set value), should we remove the sorting?\n if (!hasManualValue) {\n // Is our intention to remove?\n if (!nextSortingOrder) {\n sortAction = 'remove'\n }\n }\n }\n\n if (sortAction === 'add') {\n newSorting = [\n ...old,\n {\n id: column.id,\n desc: nextDesc,\n },\n ]\n // Take latest n columns\n newSorting.splice(\n 0,\n newSorting.length -\n (column.table.options.maxMultiSortColCount ??\n Number.MAX_SAFE_INTEGER),\n )\n } else if (sortAction === 'toggle') {\n // This flips (or sets) the\n newSorting = old.map((d) => {\n if (d.id === column.id) {\n return {\n ...d,\n desc: nextDesc,\n }\n }\n return d\n })\n } else if (sortAction === 'remove') {\n newSorting = old.filter((d) => d.id !== column.id)\n } else {\n newSorting = [\n {\n id: column.id,\n desc: nextDesc,\n },\n ]\n }\n\n return newSorting\n })\n}\n\n/**\n * Resolves the first direction used when this column begins sorting.\n *\n * Column-level `sortDescFirst` wins, then table-level `sortDescFirst`, then the\n * auto direction inferred from sampled values.\n *\n * @example\n * ```ts\n * const firstDirection = column_getFirstSortDir(column)\n * ```\n */\nexport function column_getFirstSortDir<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const sortDescFirst =\n column.columnDef.sortDescFirst ??\n column.table.options.sortDescFirst ??\n column_getAutoSortDir(column) === 'desc'\n return sortDescFirst ? 'desc' : 'asc'\n}\n\n/**\n * Resolves the next sort order for this column's toggle cycle.\n *\n * The cycle starts with the first sort direction, flips between `asc` and\n * `desc`, and can return `false` when sorting removal is enabled.\n *\n * @example\n * ```ts\n * const nextOrder = column_getNextSortingOrder(column)\n * ```\n */\nexport function column_getNextSortingOrder<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>, multi?: boolean) {\n const firstSortDirection = column_getFirstSortDir(column)\n const isSorted = column_getIsSorted(column)\n\n if (!isSorted) {\n return firstSortDirection\n }\n\n if (\n isSorted !== firstSortDirection &&\n (column.table.options.enableSortingRemoval ?? true) && // If enableSortRemove, enable in general\n (multi ? (column.table.options.enableMultiRemove ?? true) : true) // If multi, don't allow if enableMultiRemove))\n ) {\n return false\n }\n return isSorted === 'desc' ? 'asc' : 'desc'\n}\n\n/**\n * Checks whether this accessor column can participate in sorting.\n *\n * The column must have an accessor and sorting must be enabled by both the\n * column definition and table options.\n *\n * @example\n * ```ts\n * const canSort = column_getCanSort(column)\n * ```\n */\nexport function column_getCanSort<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n return (\n (column.columnDef.enableSorting ?? true) &&\n (column.table.options.enableSorting ?? true) &&\n !!column.accessorFn\n )\n}\n\n/**\n * Checks whether this column can be added to a multi-sort state.\n *\n * Column-level `enableMultiSort` wins over table-level `enableMultiSort`; if\n * neither is set, accessor columns can multi-sort by default.\n *\n * @example\n * ```ts\n * const canMultiSort = column_getCanMultiSort(column)\n * ```\n */\nexport function column_getCanMultiSort<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): boolean {\n return (\n column.columnDef.enableMultiSort ??\n column.table.options.enableMultiSort ??\n !!column.accessorFn\n )\n}\n\n/**\n * Reads this column's current sort direction.\n *\n * The result is `false` when the column is not sorted, otherwise `'asc'` or\n * `'desc'` based on the column's entry in `state.sorting`.\n *\n * @example\n * ```ts\n * const direction = column_getIsSorted(column)\n * ```\n */\nexport function column_getIsSorted<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): false | SortDirection {\n const columnSort = column.table.atoms.sorting\n ?.get()\n ?.find((d) => d.id === column.id)\n return !columnSort ? false : columnSort.desc ? 'desc' : 'asc'\n}\n\n/**\n * Finds this column's position in the ordered `state.sorting` array.\n *\n * The result is `-1` when the column is not sorted.\n *\n * @example\n * ```ts\n * const index = column_getSortIndex(column)\n * ```\n */\nexport function column_getSortIndex<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>): number {\n return (\n column.table.atoms.sorting?.get()?.findIndex((d) => d.id === column.id) ??\n -1\n )\n}\n\n/**\n * Removes this column from the sorting state.\n *\n * Other sorted columns are preserved, including their relative order.\n *\n * @example\n * ```ts\n * column_clearSorting(column)\n * ```\n */\nexport function column_clearSorting<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n // clear sorting for just 1 column\n table_setSorting(column.table, (old) =>\n old.length ? old.filter((d) => d.id !== column.id) : [],\n )\n}\n\n/**\n * Creates a header event handler that toggles this column's sorting.\n *\n * The handler ignores events when the column cannot sort, persists React-style\n * synthetic events when present, and asks `options.isMultiSortEvent` whether\n * the event should add to a multi-sort.\n *\n * @example\n * ```ts\n * const onClick = column_getToggleSortingHandler(column)\n * ```\n */\nexport function column_getToggleSortingHandler<\n TFeatures extends TableFeatures,\n TData extends RowData,\n TValue extends CellData = CellData,\n>(column: Column_Internal<TFeatures, TData, TValue>) {\n const canSort = column_getCanSort(column)\n\n return (e: unknown) => {\n if (!canSort) return\n ;(e as any).persist?.()\n column_toggleSorting(\n column,\n\n undefined,\n column_getCanMultiSort(column)\n ? column.table.options.isMultiSortEvent?.(e)\n : false,\n )\n }\n}\n"],"mappings":"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"}
@@ -69,28 +69,40 @@ function toString(a) {
69
69
  return "";
70
70
  }
71
71
  function compareAlphanumeric(aStr, bStr) {
72
- const a = aStr.split(reSplitAlphaNumeric).filter(Boolean);
73
- const b = bStr.split(reSplitAlphaNumeric).filter(Boolean);
72
+ const a = aStr.split(reSplitAlphaNumeric);
73
+ const b = bStr.split(reSplitAlphaNumeric);
74
74
  let ai = 0;
75
75
  let bi = 0;
76
76
  const aLen = a.length;
77
77
  const bLen = b.length;
78
78
  while (ai < aLen && bi < bLen) {
79
+ if (!a[ai]) {
80
+ ai++;
81
+ continue;
82
+ }
83
+ if (!b[bi]) {
84
+ bi++;
85
+ continue;
86
+ }
79
87
  const aa = a[ai++];
80
88
  const bb = b[bi++];
81
89
  const an = parseInt(aa, 10);
82
90
  const bn = parseInt(bb, 10);
83
- const combo = [an, bn].sort();
84
- if (isNaN(combo[0])) {
91
+ const aIsNaN = isNaN(an);
92
+ const bIsNaN = isNaN(bn);
93
+ if (aIsNaN && bIsNaN) {
85
94
  if (aa > bb) return 1;
86
95
  if (bb > aa) return -1;
87
96
  continue;
88
97
  }
89
- if (isNaN(combo[1])) return isNaN(an) ? -1 : 1;
98
+ if (aIsNaN || bIsNaN) return aIsNaN ? -1 : 1;
90
99
  if (an > bn) return 1;
91
100
  if (bn > an) return -1;
92
101
  }
93
- return aLen - ai - (bLen - bi);
102
+ let remaining = 0;
103
+ for (; ai < aLen; ai++) if (a[ai]) remaining++;
104
+ for (; bi < bLen; bi++) if (b[bi]) remaining--;
105
+ return remaining;
94
106
  }
95
107
  /**
96
108
  * The built-in sorting function registry.
@@ -1 +1 @@
1
- {"version":3,"file":"sortFns.cjs","names":[],"sources":["../../src/fns/sortFns.ts"],"sourcesContent":["import type { RowData } from '../types/type-utils'\nimport type { TableFeatures } from '../types/TableFeatures'\nimport type { Row } from '../types/Row'\n\n/**\n * Regular expression used to split mixed text and numeric chunks.\n *\n * The alphanumeric sort functions use these chunks for natural sorting of\n * strings like `item2` before `item10`.\n */\nexport const reSplitAlphaNumeric = /([0-9]+)/gm\n\n/**\n * Sorts rows with the built-in alphanumeric strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_alphanumeric = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareAlphanumeric(\n toString(rowA.getValue(columnId)).toLowerCase(),\n toString(rowB.getValue(columnId)).toLowerCase(),\n )\n}\n\n/**\n * Sorts rows with the built-in alphanumeric case sensitive strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_alphanumericCaseSensitive = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareAlphanumeric(\n toString(rowA.getValue(columnId)),\n toString(rowB.getValue(columnId)),\n )\n}\n\n// The text filter is more basic (less numeric support)\n// but is much faster\n/**\n * Sorts rows with the built-in text strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_text = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(\n toString(rowA.getValue(columnId)).toLowerCase(),\n toString(rowB.getValue(columnId)).toLowerCase(),\n )\n}\n\n// The text filter is more basic (less numeric support)\n// but is much faster\n/**\n * Sorts rows with the built-in text case sensitive strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_textCaseSensitive = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(\n toString(rowA.getValue(columnId)),\n toString(rowB.getValue(columnId)),\n )\n}\n\n/**\n * Sorts rows with the built-in datetime strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_datetime = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n const a: number | string = rowA.getValue(columnId)\n const b: number | string = rowB.getValue(columnId)\n\n // Can handle nullish values\n // Use > and < because == (and ===) doesn't work with\n // Date objects (would require calling getTime()).\n return a > b ? 1 : a < b ? -1 : 0\n}\n\n/**\n * Sorts rows with the built-in basic strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_basic = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(rowA.getValue(columnId), rowB.getValue(columnId))\n}\n\n// Utils\n\nfunction compareBasic(a: any, b: any) {\n return a === b ? 0 : a > b ? 1 : -1\n}\n\nfunction toString(a: any) {\n if (typeof a === 'number') {\n if (isNaN(a) || a === Infinity || a === -Infinity) {\n return ''\n }\n return String(a)\n }\n if (typeof a === 'string') {\n return a\n }\n return ''\n}\n\n// Mixed sorting is slow, but very inclusive of many edge cases.\n// It handles numbers, mixed alphanumeric combinations, and even\n// null, undefined, and Infinity\nfunction compareAlphanumeric(aStr: string, bStr: string) {\n // Split on number groups, but keep the delimiter\n // Then remove falsey split values\n const a = aStr.split(reSplitAlphaNumeric).filter(Boolean)\n const b = bStr.split(reSplitAlphaNumeric).filter(Boolean)\n\n let ai = 0\n let bi = 0\n const aLen = a.length\n const bLen = b.length\n\n while (ai < aLen && bi < bLen) {\n const aa = a[ai++]!\n const bb = b[bi++]!\n\n const an = parseInt(aa, 10)\n const bn = parseInt(bb, 10)\n\n const combo = [an, bn].sort()\n\n // Both are string\n if (isNaN(combo[0]!)) {\n if (aa > bb) {\n return 1\n }\n if (bb > aa) {\n return -1\n }\n continue\n }\n\n // One is a string, one is a number\n if (isNaN(combo[1]!)) {\n return isNaN(an) ? -1 : 1\n }\n\n // Both are numbers\n if (an > bn) {\n return 1\n }\n if (bn > an) {\n return -1\n }\n }\n\n return aLen - ai - (bLen - bi)\n}\n\n// Exports\n\n/**\n * The built-in sorting function registry.\n *\n * Pass this object to sorted row model creation or extend it with custom sorting functions.\n */\nexport const sortFns = {\n alphanumeric: sortFn_alphanumeric,\n alphanumericCaseSensitive: sortFn_alphanumericCaseSensitive,\n basic: sortFn_basic,\n datetime: sortFn_datetime,\n text: sortFn_text,\n textCaseSensitive: sortFn_textCaseSensitive,\n}\n\nexport type BuiltInSortFn = keyof typeof sortFns\n"],"mappings":";;;;;;;;AAUA,MAAa,sBAAsB;;;;;;AAOnC,MAAa,uBAIX,MACA,MACA,aACG;CACH,OAAO,oBACL,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,GAC9C,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD;AACF;;;;;;AAOA,MAAa,oCAIX,MACA,MACA,aACG;CACH,OAAO,oBACL,SAAS,KAAK,SAAS,QAAQ,CAAC,GAChC,SAAS,KAAK,SAAS,QAAQ,CAAC,CAClC;AACF;;;;;;AASA,MAAa,eAIX,MACA,MACA,aACG;CACH,OAAO,aACL,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,GAC9C,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD;AACF;;;;;;AASA,MAAa,4BAIX,MACA,MACA,aACG;CACH,OAAO,aACL,SAAS,KAAK,SAAS,QAAQ,CAAC,GAChC,SAAS,KAAK,SAAS,QAAQ,CAAC,CAClC;AACF;;;;;;AAOA,MAAa,mBAIX,MACA,MACA,aACG;CACH,MAAM,IAAqB,KAAK,SAAS,QAAQ;CACjD,MAAM,IAAqB,KAAK,SAAS,QAAQ;CAKjD,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK;AAClC;;;;;;AAOA,MAAa,gBAIX,MACA,MACA,aACG;CACH,OAAO,aAAa,KAAK,SAAS,QAAQ,GAAG,KAAK,SAAS,QAAQ,CAAC;AACtE;AAIA,SAAS,aAAa,GAAQ,GAAQ;CACpC,OAAO,MAAM,IAAI,IAAI,IAAI,IAAI,IAAI;AACnC;AAEA,SAAS,SAAS,GAAQ;CACxB,IAAI,OAAO,MAAM,UAAU;EACzB,IAAI,MAAM,CAAC,KAAK,MAAM,YAAY,MAAM,WACtC,OAAO;EAET,OAAO,OAAO,CAAC;CACjB;CACA,IAAI,OAAO,MAAM,UACf,OAAO;CAET,OAAO;AACT;AAKA,SAAS,oBAAoB,MAAc,MAAc;CAGvD,MAAM,IAAI,KAAK,MAAM,mBAAmB,CAAC,CAAC,OAAO,OAAO;CACxD,MAAM,IAAI,KAAK,MAAM,mBAAmB,CAAC,CAAC,OAAO,OAAO;CAExD,IAAI,KAAK;CACT,IAAI,KAAK;CACT,MAAM,OAAO,EAAE;CACf,MAAM,OAAO,EAAE;CAEf,OAAO,KAAK,QAAQ,KAAK,MAAM;EAC7B,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,EAAE;EAEb,MAAM,KAAK,SAAS,IAAI,EAAE;EAC1B,MAAM,KAAK,SAAS,IAAI,EAAE;EAE1B,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK;EAG5B,IAAI,MAAM,MAAM,EAAG,GAAG;GACpB,IAAI,KAAK,IACP,OAAO;GAET,IAAI,KAAK,IACP,OAAO;GAET;EACF;EAGA,IAAI,MAAM,MAAM,EAAG,GACjB,OAAO,MAAM,EAAE,IAAI,KAAK;EAI1B,IAAI,KAAK,IACP,OAAO;EAET,IAAI,KAAK,IACP,OAAO;CAEX;CAEA,OAAO,OAAO,MAAM,OAAO;AAC7B;;;;;;AASA,MAAa,UAAU;CACrB,cAAc;CACd,2BAA2B;CAC3B,OAAO;CACP,UAAU;CACV,MAAM;CACN,mBAAmB;AACrB"}
1
+ {"version":3,"file":"sortFns.cjs","names":[],"sources":["../../src/fns/sortFns.ts"],"sourcesContent":["import type { RowData } from '../types/type-utils'\nimport type { TableFeatures } from '../types/TableFeatures'\nimport type { Row } from '../types/Row'\n\n/**\n * Regular expression used to split mixed text and numeric chunks.\n *\n * The alphanumeric sort functions use these chunks for natural sorting of\n * strings like `item2` before `item10`.\n */\nexport const reSplitAlphaNumeric = /([0-9]+)/gm\n\n/**\n * Sorts rows with the built-in alphanumeric strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_alphanumeric = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareAlphanumeric(\n toString(rowA.getValue(columnId)).toLowerCase(),\n toString(rowB.getValue(columnId)).toLowerCase(),\n )\n}\n\n/**\n * Sorts rows with the built-in alphanumeric case sensitive strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_alphanumericCaseSensitive = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareAlphanumeric(\n toString(rowA.getValue(columnId)),\n toString(rowB.getValue(columnId)),\n )\n}\n\n// The text filter is more basic (less numeric support)\n// but is much faster\n/**\n * Sorts rows with the built-in text strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_text = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(\n toString(rowA.getValue(columnId)).toLowerCase(),\n toString(rowB.getValue(columnId)).toLowerCase(),\n )\n}\n\n// The text filter is more basic (less numeric support)\n// but is much faster\n/**\n * Sorts rows with the built-in text case sensitive strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_textCaseSensitive = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(\n toString(rowA.getValue(columnId)),\n toString(rowB.getValue(columnId)),\n )\n}\n\n/**\n * Sorts rows with the built-in datetime strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_datetime = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n const a: number | string = rowA.getValue(columnId)\n const b: number | string = rowB.getValue(columnId)\n\n // Can handle nullish values\n // Use > and < because == (and ===) doesn't work with\n // Date objects (would require calling getTime()).\n return a > b ? 1 : a < b ? -1 : 0\n}\n\n/**\n * Sorts rows with the built-in basic strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_basic = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(rowA.getValue(columnId), rowB.getValue(columnId))\n}\n\n// Utils\n\nfunction compareBasic(a: any, b: any) {\n return a === b ? 0 : a > b ? 1 : -1\n}\n\nfunction toString(a: any) {\n if (typeof a === 'number') {\n if (isNaN(a) || a === Infinity || a === -Infinity) {\n return ''\n }\n return String(a)\n }\n if (typeof a === 'string') {\n return a\n }\n return ''\n}\n\n// Mixed sorting is slow, but very inclusive of many edge cases.\n// It handles numbers, mixed alphanumeric combinations, and even\n// null, undefined, and Infinity\nfunction compareAlphanumeric(aStr: string, bStr: string) {\n const a = aStr.split(reSplitAlphaNumeric)\n const b = bStr.split(reSplitAlphaNumeric)\n\n let ai = 0\n let bi = 0\n const aLen = a.length\n const bLen = b.length\n\n while (ai < aLen && bi < bLen) {\n // Skip the empty boundary chunks that .filter(Boolean) used to remove\n if (!a[ai]) {\n ai++\n continue\n }\n if (!b[bi]) {\n bi++\n continue\n }\n\n const aa = a[ai++]!\n const bb = b[bi++]!\n\n // Chunks are either all-digit (parseInt always succeeds) or digit-free\n // (parseInt is always NaN), so NaN-ness fully classifies each chunk\n const an = parseInt(aa, 10)\n const bn = parseInt(bb, 10)\n\n const aIsNaN = isNaN(an)\n const bIsNaN = isNaN(bn)\n\n // Both are string\n if (aIsNaN && bIsNaN) {\n if (aa > bb) {\n return 1\n }\n if (bb > aa) {\n return -1\n }\n continue\n }\n\n // One is a string, one is a number — the string chunk sorts first\n if (aIsNaN || bIsNaN) {\n return aIsNaN ? -1 : 1\n }\n\n // Both are numbers\n if (an > bn) {\n return 1\n }\n if (bn > an) {\n return -1\n }\n }\n\n // One side is exhausted — compare the counts of remaining non-empty chunks\n let remaining = 0\n for (; ai < aLen; ai++) {\n if (a[ai]) {\n remaining++\n }\n }\n for (; bi < bLen; bi++) {\n if (b[bi]) {\n remaining--\n }\n }\n return remaining\n}\n\n// Exports\n\n/**\n * The built-in sorting function registry.\n *\n * Pass this object to sorted row model creation or extend it with custom sorting functions.\n */\nexport const sortFns = {\n alphanumeric: sortFn_alphanumeric,\n alphanumericCaseSensitive: sortFn_alphanumericCaseSensitive,\n basic: sortFn_basic,\n datetime: sortFn_datetime,\n text: sortFn_text,\n textCaseSensitive: sortFn_textCaseSensitive,\n}\n\nexport type BuiltInSortFn = keyof typeof sortFns\n"],"mappings":";;;;;;;;AAUA,MAAa,sBAAsB;;;;;;AAOnC,MAAa,uBAIX,MACA,MACA,aACG;CACH,OAAO,oBACL,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,GAC9C,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD;AACF;;;;;;AAOA,MAAa,oCAIX,MACA,MACA,aACG;CACH,OAAO,oBACL,SAAS,KAAK,SAAS,QAAQ,CAAC,GAChC,SAAS,KAAK,SAAS,QAAQ,CAAC,CAClC;AACF;;;;;;AASA,MAAa,eAIX,MACA,MACA,aACG;CACH,OAAO,aACL,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,GAC9C,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD;AACF;;;;;;AASA,MAAa,4BAIX,MACA,MACA,aACG;CACH,OAAO,aACL,SAAS,KAAK,SAAS,QAAQ,CAAC,GAChC,SAAS,KAAK,SAAS,QAAQ,CAAC,CAClC;AACF;;;;;;AAOA,MAAa,mBAIX,MACA,MACA,aACG;CACH,MAAM,IAAqB,KAAK,SAAS,QAAQ;CACjD,MAAM,IAAqB,KAAK,SAAS,QAAQ;CAKjD,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK;AAClC;;;;;;AAOA,MAAa,gBAIX,MACA,MACA,aACG;CACH,OAAO,aAAa,KAAK,SAAS,QAAQ,GAAG,KAAK,SAAS,QAAQ,CAAC;AACtE;AAIA,SAAS,aAAa,GAAQ,GAAQ;CACpC,OAAO,MAAM,IAAI,IAAI,IAAI,IAAI,IAAI;AACnC;AAEA,SAAS,SAAS,GAAQ;CACxB,IAAI,OAAO,MAAM,UAAU;EACzB,IAAI,MAAM,CAAC,KAAK,MAAM,YAAY,MAAM,WACtC,OAAO;EAET,OAAO,OAAO,CAAC;CACjB;CACA,IAAI,OAAO,MAAM,UACf,OAAO;CAET,OAAO;AACT;AAKA,SAAS,oBAAoB,MAAc,MAAc;CACvD,MAAM,IAAI,KAAK,MAAM,mBAAmB;CACxC,MAAM,IAAI,KAAK,MAAM,mBAAmB;CAExC,IAAI,KAAK;CACT,IAAI,KAAK;CACT,MAAM,OAAO,EAAE;CACf,MAAM,OAAO,EAAE;CAEf,OAAO,KAAK,QAAQ,KAAK,MAAM;EAE7B,IAAI,CAAC,EAAE,KAAK;GACV;GACA;EACF;EACA,IAAI,CAAC,EAAE,KAAK;GACV;GACA;EACF;EAEA,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,EAAE;EAIb,MAAM,KAAK,SAAS,IAAI,EAAE;EAC1B,MAAM,KAAK,SAAS,IAAI,EAAE;EAE1B,MAAM,SAAS,MAAM,EAAE;EACvB,MAAM,SAAS,MAAM,EAAE;EAGvB,IAAI,UAAU,QAAQ;GACpB,IAAI,KAAK,IACP,OAAO;GAET,IAAI,KAAK,IACP,OAAO;GAET;EACF;EAGA,IAAI,UAAU,QACZ,OAAO,SAAS,KAAK;EAIvB,IAAI,KAAK,IACP,OAAO;EAET,IAAI,KAAK,IACP,OAAO;CAEX;CAGA,IAAI,YAAY;CAChB,OAAO,KAAK,MAAM,MAChB,IAAI,EAAE,KACJ;CAGJ,OAAO,KAAK,MAAM,MAChB,IAAI,EAAE,KACJ;CAGJ,OAAO;AACT;;;;;;AASA,MAAa,UAAU;CACrB,cAAc;CACd,2BAA2B;CAC3B,OAAO;CACP,UAAU;CACV,MAAM;CACN,mBAAmB;AACrB"}
@@ -68,28 +68,40 @@ function toString(a) {
68
68
  return "";
69
69
  }
70
70
  function compareAlphanumeric(aStr, bStr) {
71
- const a = aStr.split(reSplitAlphaNumeric).filter(Boolean);
72
- const b = bStr.split(reSplitAlphaNumeric).filter(Boolean);
71
+ const a = aStr.split(reSplitAlphaNumeric);
72
+ const b = bStr.split(reSplitAlphaNumeric);
73
73
  let ai = 0;
74
74
  let bi = 0;
75
75
  const aLen = a.length;
76
76
  const bLen = b.length;
77
77
  while (ai < aLen && bi < bLen) {
78
+ if (!a[ai]) {
79
+ ai++;
80
+ continue;
81
+ }
82
+ if (!b[bi]) {
83
+ bi++;
84
+ continue;
85
+ }
78
86
  const aa = a[ai++];
79
87
  const bb = b[bi++];
80
88
  const an = parseInt(aa, 10);
81
89
  const bn = parseInt(bb, 10);
82
- const combo = [an, bn].sort();
83
- if (isNaN(combo[0])) {
90
+ const aIsNaN = isNaN(an);
91
+ const bIsNaN = isNaN(bn);
92
+ if (aIsNaN && bIsNaN) {
84
93
  if (aa > bb) return 1;
85
94
  if (bb > aa) return -1;
86
95
  continue;
87
96
  }
88
- if (isNaN(combo[1])) return isNaN(an) ? -1 : 1;
97
+ if (aIsNaN || bIsNaN) return aIsNaN ? -1 : 1;
89
98
  if (an > bn) return 1;
90
99
  if (bn > an) return -1;
91
100
  }
92
- return aLen - ai - (bLen - bi);
101
+ let remaining = 0;
102
+ for (; ai < aLen; ai++) if (a[ai]) remaining++;
103
+ for (; bi < bLen; bi++) if (b[bi]) remaining--;
104
+ return remaining;
93
105
  }
94
106
  /**
95
107
  * The built-in sorting function registry.
@@ -1 +1 @@
1
- {"version":3,"file":"sortFns.js","names":[],"sources":["../../src/fns/sortFns.ts"],"sourcesContent":["import type { RowData } from '../types/type-utils'\nimport type { TableFeatures } from '../types/TableFeatures'\nimport type { Row } from '../types/Row'\n\n/**\n * Regular expression used to split mixed text and numeric chunks.\n *\n * The alphanumeric sort functions use these chunks for natural sorting of\n * strings like `item2` before `item10`.\n */\nexport const reSplitAlphaNumeric = /([0-9]+)/gm\n\n/**\n * Sorts rows with the built-in alphanumeric strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_alphanumeric = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareAlphanumeric(\n toString(rowA.getValue(columnId)).toLowerCase(),\n toString(rowB.getValue(columnId)).toLowerCase(),\n )\n}\n\n/**\n * Sorts rows with the built-in alphanumeric case sensitive strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_alphanumericCaseSensitive = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareAlphanumeric(\n toString(rowA.getValue(columnId)),\n toString(rowB.getValue(columnId)),\n )\n}\n\n// The text filter is more basic (less numeric support)\n// but is much faster\n/**\n * Sorts rows with the built-in text strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_text = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(\n toString(rowA.getValue(columnId)).toLowerCase(),\n toString(rowB.getValue(columnId)).toLowerCase(),\n )\n}\n\n// The text filter is more basic (less numeric support)\n// but is much faster\n/**\n * Sorts rows with the built-in text case sensitive strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_textCaseSensitive = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(\n toString(rowA.getValue(columnId)),\n toString(rowB.getValue(columnId)),\n )\n}\n\n/**\n * Sorts rows with the built-in datetime strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_datetime = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n const a: number | string = rowA.getValue(columnId)\n const b: number | string = rowB.getValue(columnId)\n\n // Can handle nullish values\n // Use > and < because == (and ===) doesn't work with\n // Date objects (would require calling getTime()).\n return a > b ? 1 : a < b ? -1 : 0\n}\n\n/**\n * Sorts rows with the built-in basic strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_basic = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(rowA.getValue(columnId), rowB.getValue(columnId))\n}\n\n// Utils\n\nfunction compareBasic(a: any, b: any) {\n return a === b ? 0 : a > b ? 1 : -1\n}\n\nfunction toString(a: any) {\n if (typeof a === 'number') {\n if (isNaN(a) || a === Infinity || a === -Infinity) {\n return ''\n }\n return String(a)\n }\n if (typeof a === 'string') {\n return a\n }\n return ''\n}\n\n// Mixed sorting is slow, but very inclusive of many edge cases.\n// It handles numbers, mixed alphanumeric combinations, and even\n// null, undefined, and Infinity\nfunction compareAlphanumeric(aStr: string, bStr: string) {\n // Split on number groups, but keep the delimiter\n // Then remove falsey split values\n const a = aStr.split(reSplitAlphaNumeric).filter(Boolean)\n const b = bStr.split(reSplitAlphaNumeric).filter(Boolean)\n\n let ai = 0\n let bi = 0\n const aLen = a.length\n const bLen = b.length\n\n while (ai < aLen && bi < bLen) {\n const aa = a[ai++]!\n const bb = b[bi++]!\n\n const an = parseInt(aa, 10)\n const bn = parseInt(bb, 10)\n\n const combo = [an, bn].sort()\n\n // Both are string\n if (isNaN(combo[0]!)) {\n if (aa > bb) {\n return 1\n }\n if (bb > aa) {\n return -1\n }\n continue\n }\n\n // One is a string, one is a number\n if (isNaN(combo[1]!)) {\n return isNaN(an) ? -1 : 1\n }\n\n // Both are numbers\n if (an > bn) {\n return 1\n }\n if (bn > an) {\n return -1\n }\n }\n\n return aLen - ai - (bLen - bi)\n}\n\n// Exports\n\n/**\n * The built-in sorting function registry.\n *\n * Pass this object to sorted row model creation or extend it with custom sorting functions.\n */\nexport const sortFns = {\n alphanumeric: sortFn_alphanumeric,\n alphanumericCaseSensitive: sortFn_alphanumericCaseSensitive,\n basic: sortFn_basic,\n datetime: sortFn_datetime,\n text: sortFn_text,\n textCaseSensitive: sortFn_textCaseSensitive,\n}\n\nexport type BuiltInSortFn = keyof typeof sortFns\n"],"mappings":";;;;;;;AAUA,MAAa,sBAAsB;;;;;;AAOnC,MAAa,uBAIX,MACA,MACA,aACG;CACH,OAAO,oBACL,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,GAC9C,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD;AACF;;;;;;AAOA,MAAa,oCAIX,MACA,MACA,aACG;CACH,OAAO,oBACL,SAAS,KAAK,SAAS,QAAQ,CAAC,GAChC,SAAS,KAAK,SAAS,QAAQ,CAAC,CAClC;AACF;;;;;;AASA,MAAa,eAIX,MACA,MACA,aACG;CACH,OAAO,aACL,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,GAC9C,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD;AACF;;;;;;AASA,MAAa,4BAIX,MACA,MACA,aACG;CACH,OAAO,aACL,SAAS,KAAK,SAAS,QAAQ,CAAC,GAChC,SAAS,KAAK,SAAS,QAAQ,CAAC,CAClC;AACF;;;;;;AAOA,MAAa,mBAIX,MACA,MACA,aACG;CACH,MAAM,IAAqB,KAAK,SAAS,QAAQ;CACjD,MAAM,IAAqB,KAAK,SAAS,QAAQ;CAKjD,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK;AAClC;;;;;;AAOA,MAAa,gBAIX,MACA,MACA,aACG;CACH,OAAO,aAAa,KAAK,SAAS,QAAQ,GAAG,KAAK,SAAS,QAAQ,CAAC;AACtE;AAIA,SAAS,aAAa,GAAQ,GAAQ;CACpC,OAAO,MAAM,IAAI,IAAI,IAAI,IAAI,IAAI;AACnC;AAEA,SAAS,SAAS,GAAQ;CACxB,IAAI,OAAO,MAAM,UAAU;EACzB,IAAI,MAAM,CAAC,KAAK,MAAM,YAAY,MAAM,WACtC,OAAO;EAET,OAAO,OAAO,CAAC;CACjB;CACA,IAAI,OAAO,MAAM,UACf,OAAO;CAET,OAAO;AACT;AAKA,SAAS,oBAAoB,MAAc,MAAc;CAGvD,MAAM,IAAI,KAAK,MAAM,mBAAmB,CAAC,CAAC,OAAO,OAAO;CACxD,MAAM,IAAI,KAAK,MAAM,mBAAmB,CAAC,CAAC,OAAO,OAAO;CAExD,IAAI,KAAK;CACT,IAAI,KAAK;CACT,MAAM,OAAO,EAAE;CACf,MAAM,OAAO,EAAE;CAEf,OAAO,KAAK,QAAQ,KAAK,MAAM;EAC7B,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,EAAE;EAEb,MAAM,KAAK,SAAS,IAAI,EAAE;EAC1B,MAAM,KAAK,SAAS,IAAI,EAAE;EAE1B,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK;EAG5B,IAAI,MAAM,MAAM,EAAG,GAAG;GACpB,IAAI,KAAK,IACP,OAAO;GAET,IAAI,KAAK,IACP,OAAO;GAET;EACF;EAGA,IAAI,MAAM,MAAM,EAAG,GACjB,OAAO,MAAM,EAAE,IAAI,KAAK;EAI1B,IAAI,KAAK,IACP,OAAO;EAET,IAAI,KAAK,IACP,OAAO;CAEX;CAEA,OAAO,OAAO,MAAM,OAAO;AAC7B;;;;;;AASA,MAAa,UAAU;CACrB,cAAc;CACd,2BAA2B;CAC3B,OAAO;CACP,UAAU;CACV,MAAM;CACN,mBAAmB;AACrB"}
1
+ {"version":3,"file":"sortFns.js","names":[],"sources":["../../src/fns/sortFns.ts"],"sourcesContent":["import type { RowData } from '../types/type-utils'\nimport type { TableFeatures } from '../types/TableFeatures'\nimport type { Row } from '../types/Row'\n\n/**\n * Regular expression used to split mixed text and numeric chunks.\n *\n * The alphanumeric sort functions use these chunks for natural sorting of\n * strings like `item2` before `item10`.\n */\nexport const reSplitAlphaNumeric = /([0-9]+)/gm\n\n/**\n * Sorts rows with the built-in alphanumeric strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_alphanumeric = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareAlphanumeric(\n toString(rowA.getValue(columnId)).toLowerCase(),\n toString(rowB.getValue(columnId)).toLowerCase(),\n )\n}\n\n/**\n * Sorts rows with the built-in alphanumeric case sensitive strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_alphanumericCaseSensitive = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareAlphanumeric(\n toString(rowA.getValue(columnId)),\n toString(rowB.getValue(columnId)),\n )\n}\n\n// The text filter is more basic (less numeric support)\n// but is much faster\n/**\n * Sorts rows with the built-in text strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_text = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(\n toString(rowA.getValue(columnId)).toLowerCase(),\n toString(rowB.getValue(columnId)).toLowerCase(),\n )\n}\n\n// The text filter is more basic (less numeric support)\n// but is much faster\n/**\n * Sorts rows with the built-in text case sensitive strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_textCaseSensitive = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(\n toString(rowA.getValue(columnId)),\n toString(rowB.getValue(columnId)),\n )\n}\n\n/**\n * Sorts rows with the built-in datetime strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_datetime = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n const a: number | string = rowA.getValue(columnId)\n const b: number | string = rowB.getValue(columnId)\n\n // Can handle nullish values\n // Use > and < because == (and ===) doesn't work with\n // Date objects (would require calling getTime()).\n return a > b ? 1 : a < b ? -1 : 0\n}\n\n/**\n * Sorts rows with the built-in basic strategy.\n *\n * This comparator returns ascending-order results; descending order is applied by the sorting row model.\n */\nexport const sortFn_basic = <\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(\n rowA: Row<TFeatures, TData>,\n rowB: Row<TFeatures, TData>,\n columnId: string,\n) => {\n return compareBasic(rowA.getValue(columnId), rowB.getValue(columnId))\n}\n\n// Utils\n\nfunction compareBasic(a: any, b: any) {\n return a === b ? 0 : a > b ? 1 : -1\n}\n\nfunction toString(a: any) {\n if (typeof a === 'number') {\n if (isNaN(a) || a === Infinity || a === -Infinity) {\n return ''\n }\n return String(a)\n }\n if (typeof a === 'string') {\n return a\n }\n return ''\n}\n\n// Mixed sorting is slow, but very inclusive of many edge cases.\n// It handles numbers, mixed alphanumeric combinations, and even\n// null, undefined, and Infinity\nfunction compareAlphanumeric(aStr: string, bStr: string) {\n const a = aStr.split(reSplitAlphaNumeric)\n const b = bStr.split(reSplitAlphaNumeric)\n\n let ai = 0\n let bi = 0\n const aLen = a.length\n const bLen = b.length\n\n while (ai < aLen && bi < bLen) {\n // Skip the empty boundary chunks that .filter(Boolean) used to remove\n if (!a[ai]) {\n ai++\n continue\n }\n if (!b[bi]) {\n bi++\n continue\n }\n\n const aa = a[ai++]!\n const bb = b[bi++]!\n\n // Chunks are either all-digit (parseInt always succeeds) or digit-free\n // (parseInt is always NaN), so NaN-ness fully classifies each chunk\n const an = parseInt(aa, 10)\n const bn = parseInt(bb, 10)\n\n const aIsNaN = isNaN(an)\n const bIsNaN = isNaN(bn)\n\n // Both are string\n if (aIsNaN && bIsNaN) {\n if (aa > bb) {\n return 1\n }\n if (bb > aa) {\n return -1\n }\n continue\n }\n\n // One is a string, one is a number — the string chunk sorts first\n if (aIsNaN || bIsNaN) {\n return aIsNaN ? -1 : 1\n }\n\n // Both are numbers\n if (an > bn) {\n return 1\n }\n if (bn > an) {\n return -1\n }\n }\n\n // One side is exhausted — compare the counts of remaining non-empty chunks\n let remaining = 0\n for (; ai < aLen; ai++) {\n if (a[ai]) {\n remaining++\n }\n }\n for (; bi < bLen; bi++) {\n if (b[bi]) {\n remaining--\n }\n }\n return remaining\n}\n\n// Exports\n\n/**\n * The built-in sorting function registry.\n *\n * Pass this object to sorted row model creation or extend it with custom sorting functions.\n */\nexport const sortFns = {\n alphanumeric: sortFn_alphanumeric,\n alphanumericCaseSensitive: sortFn_alphanumericCaseSensitive,\n basic: sortFn_basic,\n datetime: sortFn_datetime,\n text: sortFn_text,\n textCaseSensitive: sortFn_textCaseSensitive,\n}\n\nexport type BuiltInSortFn = keyof typeof sortFns\n"],"mappings":";;;;;;;AAUA,MAAa,sBAAsB;;;;;;AAOnC,MAAa,uBAIX,MACA,MACA,aACG;CACH,OAAO,oBACL,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,GAC9C,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD;AACF;;;;;;AAOA,MAAa,oCAIX,MACA,MACA,aACG;CACH,OAAO,oBACL,SAAS,KAAK,SAAS,QAAQ,CAAC,GAChC,SAAS,KAAK,SAAS,QAAQ,CAAC,CAClC;AACF;;;;;;AASA,MAAa,eAIX,MACA,MACA,aACG;CACH,OAAO,aACL,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,GAC9C,SAAS,KAAK,SAAS,QAAQ,CAAC,CAAC,CAAC,YAAY,CAChD;AACF;;;;;;AASA,MAAa,4BAIX,MACA,MACA,aACG;CACH,OAAO,aACL,SAAS,KAAK,SAAS,QAAQ,CAAC,GAChC,SAAS,KAAK,SAAS,QAAQ,CAAC,CAClC;AACF;;;;;;AAOA,MAAa,mBAIX,MACA,MACA,aACG;CACH,MAAM,IAAqB,KAAK,SAAS,QAAQ;CACjD,MAAM,IAAqB,KAAK,SAAS,QAAQ;CAKjD,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK;AAClC;;;;;;AAOA,MAAa,gBAIX,MACA,MACA,aACG;CACH,OAAO,aAAa,KAAK,SAAS,QAAQ,GAAG,KAAK,SAAS,QAAQ,CAAC;AACtE;AAIA,SAAS,aAAa,GAAQ,GAAQ;CACpC,OAAO,MAAM,IAAI,IAAI,IAAI,IAAI,IAAI;AACnC;AAEA,SAAS,SAAS,GAAQ;CACxB,IAAI,OAAO,MAAM,UAAU;EACzB,IAAI,MAAM,CAAC,KAAK,MAAM,YAAY,MAAM,WACtC,OAAO;EAET,OAAO,OAAO,CAAC;CACjB;CACA,IAAI,OAAO,MAAM,UACf,OAAO;CAET,OAAO;AACT;AAKA,SAAS,oBAAoB,MAAc,MAAc;CACvD,MAAM,IAAI,KAAK,MAAM,mBAAmB;CACxC,MAAM,IAAI,KAAK,MAAM,mBAAmB;CAExC,IAAI,KAAK;CACT,IAAI,KAAK;CACT,MAAM,OAAO,EAAE;CACf,MAAM,OAAO,EAAE;CAEf,OAAO,KAAK,QAAQ,KAAK,MAAM;EAE7B,IAAI,CAAC,EAAE,KAAK;GACV;GACA;EACF;EACA,IAAI,CAAC,EAAE,KAAK;GACV;GACA;EACF;EAEA,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,EAAE;EAIb,MAAM,KAAK,SAAS,IAAI,EAAE;EAC1B,MAAM,KAAK,SAAS,IAAI,EAAE;EAE1B,MAAM,SAAS,MAAM,EAAE;EACvB,MAAM,SAAS,MAAM,EAAE;EAGvB,IAAI,UAAU,QAAQ;GACpB,IAAI,KAAK,IACP,OAAO;GAET,IAAI,KAAK,IACP,OAAO;GAET;EACF;EAGA,IAAI,UAAU,QACZ,OAAO,SAAS,KAAK;EAIvB,IAAI,KAAK,IACP,OAAO;EAET,IAAI,KAAK,IACP,OAAO;CAEX;CAGA,IAAI,YAAY;CAChB,OAAO,KAAK,MAAM,MAChB,IAAI,EAAE,KACJ;CAGJ,OAAO,KAAK,MAAM,MAChB,IAAI,EAAE,KACJ;CAGJ,OAAO;AACT;;;;;;AASA,MAAa,UAAU;CACrB,cAAc;CACd,2BAA2B;CAC3B,OAAO;CACP,UAAU;CACV,MAAM;CACN,mBAAmB;AACrB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/table-core",
3
- "version": "9.0.0-beta.5",
3
+ "version": "9.0.0-beta.7",
4
4
  "description": "Headless UI for building powerful tables & datagrids for TS/JS.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -89,8 +89,6 @@ export function column_getAutoSortFn<
89
89
  const sortFns: Record<string, SortFn<TFeatures, TData>> | undefined =
90
90
  column.table._rowModelFns.sortFns
91
91
 
92
- let sortFn: SortFn<TFeatures, TData> | undefined
93
-
94
92
  const firstRows = column.table.getFilteredRowModel().flatRows.slice(0, 10)
95
93
 
96
94
  let isString = false
@@ -99,23 +97,27 @@ export function column_getAutoSortFn<
99
97
  const value = firstRows[i]!.getValue(column.id)
100
98
 
101
99
  if (Object.prototype.toString.call(value) === '[object Date]') {
102
- sortFn = sortFns?.datetime
100
+ if (sortFns?.datetime) {
101
+ return sortFns.datetime
102
+ }
103
103
  }
104
104
 
105
105
  if (typeof value === 'string') {
106
106
  isString = true
107
107
 
108
108
  if (value.split(reSplitAlphaNumeric).length > 1) {
109
- sortFn = sortFns?.alphanumeric
109
+ if (sortFns?.alphanumeric) {
110
+ return sortFns.alphanumeric
111
+ }
110
112
  }
111
113
  }
112
114
  }
113
115
 
114
116
  if (isString) {
115
- sortFn = sortFns?.text
117
+ return sortFns?.text ?? sortFn_basic
116
118
  }
117
119
 
118
- return sortFn ?? sortFn_basic
120
+ return sortFn_basic
119
121
  }
120
122
 
121
123
  /**
@@ -151,10 +151,8 @@ function toString(a: any) {
151
151
  // It handles numbers, mixed alphanumeric combinations, and even
152
152
  // null, undefined, and Infinity
153
153
  function compareAlphanumeric(aStr: string, bStr: string) {
154
- // Split on number groups, but keep the delimiter
155
- // Then remove falsey split values
156
- const a = aStr.split(reSplitAlphaNumeric).filter(Boolean)
157
- const b = bStr.split(reSplitAlphaNumeric).filter(Boolean)
154
+ const a = aStr.split(reSplitAlphaNumeric)
155
+ const b = bStr.split(reSplitAlphaNumeric)
158
156
 
159
157
  let ai = 0
160
158
  let bi = 0
@@ -162,16 +160,29 @@ function compareAlphanumeric(aStr: string, bStr: string) {
162
160
  const bLen = b.length
163
161
 
164
162
  while (ai < aLen && bi < bLen) {
163
+ // Skip the empty boundary chunks that .filter(Boolean) used to remove
164
+ if (!a[ai]) {
165
+ ai++
166
+ continue
167
+ }
168
+ if (!b[bi]) {
169
+ bi++
170
+ continue
171
+ }
172
+
165
173
  const aa = a[ai++]!
166
174
  const bb = b[bi++]!
167
175
 
176
+ // Chunks are either all-digit (parseInt always succeeds) or digit-free
177
+ // (parseInt is always NaN), so NaN-ness fully classifies each chunk
168
178
  const an = parseInt(aa, 10)
169
179
  const bn = parseInt(bb, 10)
170
180
 
171
- const combo = [an, bn].sort()
181
+ const aIsNaN = isNaN(an)
182
+ const bIsNaN = isNaN(bn)
172
183
 
173
184
  // Both are string
174
- if (isNaN(combo[0]!)) {
185
+ if (aIsNaN && bIsNaN) {
175
186
  if (aa > bb) {
176
187
  return 1
177
188
  }
@@ -181,9 +192,9 @@ function compareAlphanumeric(aStr: string, bStr: string) {
181
192
  continue
182
193
  }
183
194
 
184
- // One is a string, one is a number
185
- if (isNaN(combo[1]!)) {
186
- return isNaN(an) ? -1 : 1
195
+ // One is a string, one is a number — the string chunk sorts first
196
+ if (aIsNaN || bIsNaN) {
197
+ return aIsNaN ? -1 : 1
187
198
  }
188
199
 
189
200
  // Both are numbers
@@ -195,7 +206,19 @@ function compareAlphanumeric(aStr: string, bStr: string) {
195
206
  }
196
207
  }
197
208
 
198
- return aLen - ai - (bLen - bi)
209
+ // One side is exhausted compare the counts of remaining non-empty chunks
210
+ let remaining = 0
211
+ for (; ai < aLen; ai++) {
212
+ if (a[ai]) {
213
+ remaining++
214
+ }
215
+ }
216
+ for (; bi < bLen; bi++) {
217
+ if (b[bi]) {
218
+ remaining--
219
+ }
220
+ }
221
+ return remaining
199
222
  }
200
223
 
201
224
  // Exports