feat(Pairing): add nextFocalId cycle math and expose slice API

This commit is contained in:
Ilia Mashkov
2026-06-24 13:48:50 +03:00
parent 91bb046339
commit f29e0b0c7c
4 changed files with 65 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
export { comboKey } from './comboKey/comboKey';
export { createPairing } from './createPairing/createPairing';
export { nextFocalId } from './nextFocalId/nextFocalId';
@@ -0,0 +1,32 @@
import {
describe,
expect,
it,
} from 'vitest';
import { nextFocalId } from './nextFocalId';
const ids = ['a', 'b', 'c'];
describe('nextFocalId', () => {
it('steps forward', () => {
expect(nextFocalId(ids, 'a', 1)).toBe('b');
});
it('steps backward', () => {
expect(nextFocalId(ids, 'b', -1)).toBe('a');
});
it('wraps forward at the end', () => {
expect(nextFocalId(ids, 'c', 1)).toBe('a');
});
it('wraps backward at the start', () => {
expect(nextFocalId(ids, 'a', -1)).toBe('c');
});
it('returns the only id when list has one', () => {
expect(nextFocalId(['solo'], 'solo', 1)).toBe('solo');
});
it('returns current when focal id is absent', () => {
expect(nextFocalId(ids, 'missing', 1)).toBe('missing');
});
it('returns null for an empty list', () => {
expect(nextFocalId([], 'x', 1)).toBeNull();
});
});
@@ -0,0 +1,21 @@
/**
* The id one step from `currentId` in board order, wrapping at both ends.
*
* @param orderedIds - Pairing ids in board order.
* @param currentId - The currently focal id to step from.
* @param direction - +1 for next, -1 for previous.
* @returns The neighbouring id (wrapped), `currentId` unchanged if it isn't in
* the list, or null for an empty list.
*/
export function nextFocalId(orderedIds: string[], currentId: string, direction: 1 | -1): string | null {
if (orderedIds.length === 0) {
return null;
}
const i = orderedIds.indexOf(currentId);
if (i === -1) {
return currentId;
}
const len = orderedIds.length;
const next = (i + direction + len) % len;
return orderedIds[next];
}
+9
View File
@@ -0,0 +1,9 @@
export {
comboKey,
createPairing,
nextFocalId,
} from './domain';
export type {
Pairing,
Role,
} from './model/types';