72 lines
2.5 KiB
TypeScript
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;
|
|
};
|