Files
tnsu-approval-route/frontend/src/webparts/approvalAdmin/utils/orderBlocks.ts
T
2026-08-07 11:06:15 +05:00

70 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Общая механика «шагов, которые могут идти параллельно» — 1:1 повторяет
// request-portal/src/webparts/requestPortal/shared/approval/useMatrixEditor.ts
// (blockRanges/swapBlocks/withOrders/markParallel), только обобщена под
// generic-тип с полями order/parallelWithPrev, чтобы работать и с
// ManagedApprovalRow здесь, и не тащить зависимость на request-portal.
//
// Параллельность кодируется ЧЕРЕЗ РАВНЫЙ order у соседних строк — отдельного
// флага в SharePoint нет (см. FIELDS в requestManageService.ts: там только
// StepOrder). parallelWithPrev — это производное поле, вычисляемое на клиенте
// из совпадения order с предыдущей строкой (см. markParallel), а не то, что
// хранится в списке.
export interface IOrderedItem {
order: number;
parallelWithPrev?: boolean;
}
export interface IBlockRange {
start: number;
end: number;
}
/** Разбивает строки (уже отсортированные по order) на шаги — блоки с общим order. */
export function blockRanges<T extends IOrderedItem>(rows: T[]): IBlockRange[] {
const ranges: IBlockRange[] = [];
rows.forEach((r, i) => {
if (i === 0 || !r.parallelWithPrev) ranges.push({ start: i, end: i });
else ranges[ranges.length - 1].end = i;
});
return ranges;
}
export function blockIndexOf(ranges: IBlockRange[], row: number): number {
for (let i = 0; i < ranges.length; i += 1) {
if (row >= ranges[i].start && row <= ranges[i].end) return i;
}
return -1;
}
/** Меняет местами два блока (диапазона индексов), не трогая порядок строк внутри них. */
export function swapBlocks<T>(rows: T[], a: IBlockRange, b: IBlockRange): T[] {
return [
...rows.slice(0, a.start),
...rows.slice(b.start, b.end + 1),
...rows.slice(a.start, a.end + 1),
...rows.slice(b.end + 1)
];
}
/**
* Пересчитывает order начиная с floor+1: у параллельных строк (parallelWithPrev)
* order общий с предыдущей, у остальных — на единицу больше. Вызывается после
* КАЖДОГО перемещения/переключения параллельности, иначе order разъедется с тем,
* что показано на экране. floor — order последней ЗАБЛОКИРОВАННОЙ (уже решённой)
* строки фазы; строки после неё не могут получить order <= floor — это и есть
* гарантия «нельзя поставить согласующего выше того, кто уже одобрил».
*/
export function withOrdersFrom<T extends IOrderedItem>(rows: T[], floor: number): T[] {
let order = floor;
return rows.map((r, i) => {
const parallel = i > 0 && !!r.parallelWithPrev;
if (!parallel) order += 1;
return { ...r, order, parallelWithPrev: parallel };
});
}
/** Заполняет parallelWithPrev по факту совпадения order с предыдущей строкой (строки уже отсортированы по order). */
export function markParallel<T extends IOrderedItem>(rows: T[]): T[] {
return rows.map((r, i) => ({ ...r, parallelWithPrev: i > 0 && r.order === rows[i - 1].order }));
}