Files
frontend/apps/sn/app/(with-base-layout)/oper/tableSort.ts
T

72 lines
2.5 KiB
TypeScript

import type { UiHeadProps, UiTableSort } from "@common/business_components/ui";
const TABLE_SORT_COLLATOR = new Intl.Collator("ko-KR", {
numeric: true,
sensitivity: "base",
});
const isEmptySortValue = (value: unknown) =>
value === null ||
value === undefined ||
String(value).trim() === "" ||
String(value).trim() === "-";
const toComparableNumber = (value: unknown) => {
const normalized = String(value ?? "")
.trim()
.replace(/,/g, "")
.replace(/%$/, "");
if (!/^-?\d+(?:\.\d+)?$/.test(normalized)) return undefined;
const number = Number(normalized);
return Number.isFinite(number) ? number : undefined;
};
/** 업로드된 엑셀의 실제 데이터 컬럼만 공통 UiTable 정렬 대상으로 만든다. */
export const makeSortableHeaders = <T extends UiHeadProps>(headers: T[]) =>
headers.map((header) => ({ ...header, sortable: true }));
/** 서버 페이징이 없는 엑셀 상세 그리드의 전체 행을 안정 정렬한다. */
export const sortLocalTableRows = <T extends Record<string, unknown>>(
rows: T[],
sort?: UiTableSort,
) => {
if (!sort) return rows;
return rows
.map((row, originalIndex) => ({ row, originalIndex }))
.sort((leftEntry, rightEntry) => {
const leftValue = leftEntry.row[sort.key];
const rightValue = rightEntry.row[sort.key];
const leftEmpty = isEmptySortValue(leftValue);
const rightEmpty = isEmptySortValue(rightValue);
if (leftEmpty || rightEmpty) {
if (leftEmpty && rightEmpty) return leftEntry.originalIndex - rightEntry.originalIndex;
return leftEmpty ? 1 : -1;
}
const leftNumber = toComparableNumber(leftValue);
const rightNumber = toComparableNumber(rightValue);
const comparison =
leftNumber !== undefined && rightNumber !== undefined
? leftNumber - rightNumber
: TABLE_SORT_COLLATOR.compare(String(leftValue), String(rightValue));
if (comparison === 0) return leftEntry.originalIndex - rightEntry.originalIndex;
return sort.direction === "asc" ? comparison : -comparison;
})
.map(({ row }) => row);
};
/** children으로 다단 헤더를 전달하는 UiTable에서도 동일한 3상태 정렬을 사용한다. */
export const getNextTableSort = (
head: UiHeadProps,
currentSort?: UiTableSort,
): UiTableSort | undefined => {
const key = head.sortKey ?? head.id;
if (currentSort?.key !== key) return { key, direction: "asc" };
if (currentSort.direction === "asc") return { key, direction: "desc" };
return undefined;
};