mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 14:39:06 +00:00
refactor: entry actions in cuesheet
fix: insert entry before fix: avoid double submit on enter fix: move events in the rundown fix: clone groups
This commit is contained in:
committed by
Carlos Valente
parent
8ad260d28a
commit
24a3823d3b
@@ -42,7 +42,7 @@ import { cloneEvent } from '../../common/utils/clone';
|
||||
import QuickAddBlock from './quick-add-block/QuickAddBlock';
|
||||
import BlockEnd from './rundown-block/BlockEnd';
|
||||
import RundownBlock from './rundown-block/RundownBlock';
|
||||
import { makeRundownMetadata, makeSortableList, moveDown, moveUp } from './rundown.utils';
|
||||
import { makeRundownMetadata, makeSortableList } from './rundown.utils';
|
||||
import RundownEmpty from './RundownEmpty';
|
||||
import { useEventSelection } from './useEventSelection';
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function Rundown({ data }: RundownProps) {
|
||||
defaultValue: [],
|
||||
});
|
||||
|
||||
const { addEntry, reorderEntry, deleteEntry } = useEntryActions();
|
||||
const { addEntry, deleteEntry, move, reorderEntry } = useEntryActions();
|
||||
|
||||
const { entryCopyId, setEntryCopyId } = useEntryCopy();
|
||||
|
||||
@@ -218,26 +218,18 @@ export default function Rundown({ data }: RundownProps) {
|
||||
);
|
||||
|
||||
const moveEntry = useCallback(
|
||||
(cursor: EntryId | null, direction: 'up' | 'down') => {
|
||||
if (sortableData.length < 2 || cursor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { destinationId, order, isBlock } =
|
||||
direction === 'up' ? moveUp(cursor, sortableData, entries) : moveDown(cursor, sortableData, entries);
|
||||
|
||||
if (!destinationId) {
|
||||
async (cursor: EntryId | null, direction: 'up' | 'down') => {
|
||||
if (cursor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const movedIntoBlockId = await move(cursor, direction);
|
||||
// if we are moving into a block, we need to make sure it is expanded
|
||||
if (isBlock) {
|
||||
handleCollapseGroup(false, destinationId);
|
||||
if (movedIntoBlockId) {
|
||||
handleCollapseGroup(false, movedIntoBlockId);
|
||||
}
|
||||
|
||||
reorderEntry(cursor, destinationId, order as 'before' | 'after' | 'insert');
|
||||
},
|
||||
[sortableData, entries, reorderEntry, handleCollapseGroup],
|
||||
[handleCollapseGroup, move],
|
||||
);
|
||||
|
||||
// shortcuts
|
||||
|
||||
@@ -334,66 +334,199 @@ describe('makeSortableList()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('moveUp()', () => {
|
||||
const sortableData = ['event1', 'event2', 'block1', 'event11', 'end-block1', 'block2', 'end-block2', 'event3'];
|
||||
const entries = {
|
||||
event1: { type: 'event', id: 'event1', parent: null } as OntimeEvent,
|
||||
event2: { type: 'event', id: 'event2', parent: null } as OntimeEvent,
|
||||
block1: { type: 'block', id: 'block1', entries: ['event3'] } as OntimeBlock,
|
||||
event11: { type: 'event', id: 'event11', parent: 'block1' } as OntimeEvent,
|
||||
block2: { type: 'block', id: 'block2', entries: [] as EntryId[] } as OntimeBlock,
|
||||
event3: { type: 'event', id: 'event3', parent: null } as OntimeEvent,
|
||||
const rundown = {
|
||||
entries: {
|
||||
'1': { id: '1', type: 'event', parent: null } as OntimeEvent,
|
||||
'2': { id: '2', type: 'event', parent: null } as OntimeEvent,
|
||||
'3': { id: '3', type: 'event', parent: null } as OntimeEvent,
|
||||
block: { id: 'block', type: 'block', entries: ['11', '12'] } as OntimeBlock,
|
||||
'11': { id: '11', type: 'event', parent: 'block' } as OntimeEvent,
|
||||
'12': { id: '12', type: 'event', parent: 'block' } as OntimeEvent,
|
||||
'4': { id: '4', type: 'event', parent: null } as OntimeEvent,
|
||||
block2: { id: 'block2', type: 'block', entries: [] as EntryId[] } as OntimeBlock,
|
||||
'5': { id: '5', type: 'event', parent: null } as OntimeEvent,
|
||||
},
|
||||
order: ['1', '2', '3', 'block', '4', 'block2', '5'],
|
||||
flatOrder: ['1', '2', '3', 'block', '11', '12', '4', 'block2', '5'],
|
||||
};
|
||||
|
||||
it('moves an event up in the list', () => {
|
||||
const result = moveUp('event2', sortableData, entries);
|
||||
expect(result).toStrictEqual({ destinationId: 'event1', order: 'before', isBlock: false });
|
||||
it('moving the first event is a noop', () => {
|
||||
expect(moveUp('1', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: null,
|
||||
order: 'before',
|
||||
});
|
||||
});
|
||||
|
||||
it.todo('disallows nesting blocks', () => {
|
||||
const result = moveUp('block2', sortableData, entries);
|
||||
expect(result).toStrictEqual({ destinationId: 'block1', order: 'before', isBlock: false });
|
||||
it('moves an entry up in the rundown', () => {
|
||||
expect(moveUp('2', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '1',
|
||||
order: 'before',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves an event into a block', () => {
|
||||
const result = moveUp('event3', sortableData, entries);
|
||||
expect(result).toStrictEqual({ destinationId: 'block2', order: 'insert', isBlock: true });
|
||||
it('moves an entry up inside a block', () => {
|
||||
expect(moveUp('12', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '11',
|
||||
order: 'before',
|
||||
});
|
||||
});
|
||||
|
||||
it('moving up from top is noop', () => {
|
||||
const result = moveUp('event1', sortableData, entries);
|
||||
expect(result).toMatchObject({ destinationId: null });
|
||||
it('moves an entry up into an empty group', () => {
|
||||
expect(moveUp('5', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: 'block2',
|
||||
order: 'insert',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves an entry up into a group', () => {
|
||||
expect(moveUp('4', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '12',
|
||||
order: 'after',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves an entry up out of a group', () => {
|
||||
expect(moveUp('11', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: 'block',
|
||||
order: 'before',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves a block in the rundown', () => {
|
||||
expect(moveUp('block', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '3',
|
||||
order: 'before',
|
||||
});
|
||||
});
|
||||
|
||||
it('swaps two blocks', () => {
|
||||
const rundown = {
|
||||
entries: {
|
||||
block: { id: 'block', type: 'block', entries: ['11'] } as OntimeBlock,
|
||||
'11': { id: '11', type: 'event', parent: 'block' } as OntimeEvent,
|
||||
block2: { id: 'block2', type: 'block', entries: [] as EntryId[] } as OntimeBlock,
|
||||
},
|
||||
order: ['block', 'block2'],
|
||||
flatOrder: ['block', '11', 'block2'],
|
||||
};
|
||||
expect(moveUp('block2', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: 'block',
|
||||
order: 'before',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves before a block', () => {
|
||||
const rundown = {
|
||||
entries: {
|
||||
block: { id: 'block', type: 'block', entries: ['11'] } as OntimeBlock,
|
||||
'11': { id: '11', type: 'event', parent: 'block' } as OntimeEvent,
|
||||
},
|
||||
order: ['block'],
|
||||
flatOrder: ['block', '11'],
|
||||
};
|
||||
expect(moveUp('11', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: 'block',
|
||||
order: 'before',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('moveDown()', () => {
|
||||
const sortableData = ['event1', 'event2', 'block1', 'event11', 'end-block1', 'block2', 'end-block2', 'event3'];
|
||||
const entries = {
|
||||
event1: { type: 'event', id: 'event1', parent: null } as OntimeEvent,
|
||||
event2: { type: 'event', id: 'event2', parent: null } as OntimeEvent,
|
||||
block1: { type: 'block', id: 'block1', entries: ['event11'] } as OntimeBlock,
|
||||
event11: { type: 'event', id: 'event11', parent: 'block1' } as OntimeEvent,
|
||||
block2: { type: 'block', id: 'block2', entries: [] as EntryId[] } as OntimeBlock,
|
||||
event3: { type: 'event', id: 'event3', parent: null } as OntimeEvent,
|
||||
const rundown = {
|
||||
entries: {
|
||||
'1': { id: '1', type: 'event', parent: null } as OntimeEvent,
|
||||
'2': { id: '2', type: 'event', parent: null } as OntimeEvent,
|
||||
'3': { id: '3', type: 'event', parent: null } as OntimeEvent,
|
||||
block: { id: 'block', type: 'block', entries: ['11', '12'] } as OntimeBlock,
|
||||
'11': { id: '11', type: 'event', parent: 'block' } as OntimeEvent,
|
||||
'12': { id: '12', type: 'event', parent: 'block' } as OntimeEvent,
|
||||
'4': { id: '4', type: 'event', parent: null } as OntimeEvent,
|
||||
block2: { id: 'block2', type: 'block', entries: [] as EntryId[] } as OntimeBlock,
|
||||
'5': { id: '5', type: 'event', parent: null } as OntimeEvent,
|
||||
},
|
||||
order: ['1', '2', '3', 'block', '4', 'block2', '5'],
|
||||
flatOrder: ['1', '2', '3', 'block', '11', '12', '4', 'block2', '5'],
|
||||
};
|
||||
|
||||
it('moves an event down in the list', () => {
|
||||
const result = moveDown('event1', sortableData, entries);
|
||||
expect(result).toStrictEqual({ destinationId: 'event2', order: 'after', isBlock: false });
|
||||
it('moving the last event is a noop', () => {
|
||||
expect(moveDown('5', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: null,
|
||||
order: 'after',
|
||||
});
|
||||
});
|
||||
|
||||
it.todo('disallows nesting blocks', () => {
|
||||
const result = moveDown('block1', sortableData, entries);
|
||||
expect(result).toStrictEqual({ destinationId: 'block2', order: 'before', isBlock: false });
|
||||
it('moves an entry down in the rundown', () => {
|
||||
expect(moveDown('2', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '3',
|
||||
order: 'after',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves an event into a block', () => {
|
||||
const result = moveDown('event2', sortableData, entries);
|
||||
expect(result).toStrictEqual({ destinationId: 'event11', order: 'before', isBlock: true });
|
||||
it('moves an entry down inside a block', () => {
|
||||
expect(moveDown('11', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '12',
|
||||
order: 'after',
|
||||
});
|
||||
});
|
||||
|
||||
it('moving down from bottom is noop', () => {
|
||||
const result = moveDown('event3', sortableData, entries);
|
||||
expect(result).toMatchObject({ destinationId: null });
|
||||
it('moves an entry down into an empty group', () => {
|
||||
expect(moveDown('4', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: 'block2',
|
||||
order: 'insert',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves an entry down out of a group', () => {
|
||||
expect(moveDown('12', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '4',
|
||||
order: 'after',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves an entry down into a group', () => {
|
||||
expect(moveDown('3', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '11',
|
||||
order: 'before',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves a block in the rundown', () => {
|
||||
expect(moveDown('block', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: '4',
|
||||
order: 'after',
|
||||
});
|
||||
});
|
||||
|
||||
it('swaps two blocks', () => {
|
||||
const rundown = {
|
||||
entries: {
|
||||
block: { id: 'block', type: 'block', entries: ['11'] } as OntimeBlock,
|
||||
'11': { id: '11', type: 'event', parent: 'block' } as OntimeEvent,
|
||||
block2: { id: 'block2', type: 'block', entries: [] as EntryId[] } as OntimeBlock,
|
||||
},
|
||||
order: ['block', 'block2'],
|
||||
flatOrder: ['block', '11', 'block2'],
|
||||
};
|
||||
expect(moveDown('block', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: 'block2',
|
||||
order: 'after',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves after a block', () => {
|
||||
const rundown = {
|
||||
entries: {
|
||||
block: { id: 'block', type: 'block', entries: ['11'] } as OntimeBlock,
|
||||
'11': { id: '11', type: 'event', parent: 'block' } as OntimeEvent,
|
||||
},
|
||||
order: ['block'],
|
||||
flatOrder: ['block', '11'],
|
||||
};
|
||||
expect(moveDown('11', rundown.flatOrder, rundown.entries)).toStrictEqual({
|
||||
destinationId: 'block',
|
||||
order: 'after',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -165,101 +165,136 @@ export function canDrop(targetType?: SupportedEntry, targetParent?: EntryId | nu
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates destinations for an entry moving one position up in the rundown
|
||||
* - Handles noops
|
||||
* - Handles moving in and out of blocks
|
||||
* TODO: handle moving blocks
|
||||
* calculates destinations for an entry moving one position up in the rundown
|
||||
* @returns An object describing how to move the entry:
|
||||
* - destinationId: The target entry ID (null if no movement possible)
|
||||
* - order: How to position relative to the destination:
|
||||
* - 'before': Place before the destination
|
||||
* - 'after': Place after the destination
|
||||
* - 'insert': Insert into the destination (for blocks)
|
||||
*/
|
||||
export function moveUp(entryId: EntryId, sortableData: EntryId[], entries: RundownEntries) {
|
||||
const previousEntryId = getPreviousId(entryId, sortableData);
|
||||
export function moveUp(
|
||||
entryId: EntryId,
|
||||
flatOrder: EntryId[],
|
||||
entries: RundownEntries,
|
||||
): { destinationId: EntryId | null; order: 'before' | 'after' | 'insert' } {
|
||||
const currentEntry = entries[entryId];
|
||||
const currentIndex = flatOrder.indexOf(entryId);
|
||||
const previousEntryId = flatOrder[currentIndex - 1];
|
||||
|
||||
// the user is moving up at the top of the list
|
||||
// 1. moving at the top of the list
|
||||
if (!previousEntryId) {
|
||||
return { destinationId: null, order: 'before', isBlock: false };
|
||||
}
|
||||
|
||||
if (previousEntryId.startsWith('end-')) {
|
||||
const entry = entries[entryId];
|
||||
if (isOntimeBlock(entry)) {
|
||||
// if we are moving a block, we cannot insert it
|
||||
return { destinationId: previousEntryId.replace('end-', ''), order: 'before', isBlock: false };
|
||||
// 1a. we are in a block and need to move outside of it
|
||||
if ('parent' in currentEntry && currentEntry.parent !== null) {
|
||||
return { destinationId: currentEntry.parent, order: 'before' };
|
||||
}
|
||||
// insert in the block ID will add to the end of the block events
|
||||
return { destinationId: previousEntryId.replace('end-', ''), order: 'insert', isBlock: true };
|
||||
// 1b. we are at the start of the rundown, no movement possible
|
||||
return { destinationId: null, order: 'before' };
|
||||
}
|
||||
|
||||
// @ts-expect-error -- we safeguard the entry not having a parent property
|
||||
return { destinationId: previousEntryId, order: 'before', isBlock: Boolean(entries[previousEntryId]?.parent) };
|
||||
// 2. moving a block (always moves at top level)
|
||||
if (isOntimeBlock(currentEntry)) {
|
||||
// 21. if previous entry is inside a block, swap with parent
|
||||
const previousEntry = entries[previousEntryId];
|
||||
if ('parent' in previousEntry && previousEntry.parent !== null) {
|
||||
return { destinationId: previousEntry.parent, order: 'before' };
|
||||
}
|
||||
|
||||
// 2b. previous entry is at top level, we just swap places
|
||||
return { destinationId: previousEntryId, order: 'before' };
|
||||
}
|
||||
|
||||
const previousEntry = entries[previousEntryId];
|
||||
const currentEntryParent = currentEntry.parent;
|
||||
|
||||
// 3. moving in and out of a block
|
||||
if (isOntimeBlock(previousEntry)) {
|
||||
// 3a. if we're not already in the block, move into it
|
||||
if (currentEntryParent === null) {
|
||||
return { destinationId: previousEntryId, order: 'insert' };
|
||||
}
|
||||
// 3b. otherwise, move before the block
|
||||
return { destinationId: previousEntryId, order: 'before' };
|
||||
}
|
||||
|
||||
// 4. moving into the same block as previous entry
|
||||
if (isOntimeEvent(previousEntry) && previousEntry.parent !== null && currentEntryParent === null) {
|
||||
return { destinationId: previousEntryId, order: 'after' };
|
||||
}
|
||||
|
||||
// default - swap positions with previous entry
|
||||
return { destinationId: previousEntryId, order: 'before' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates destinations for an entry moving one position down in the rundown
|
||||
* - Handles noops
|
||||
* - Handles moving in and out of blocks
|
||||
* TODO: handle moving blocks
|
||||
* calculates destinations for an entry moving one position down in the rundown
|
||||
* @returns An object describing how to move the entry:
|
||||
* - destinationId: The target entry ID (null if no movement possible)
|
||||
* - order: How to position relative to the destination:
|
||||
* - 'before': Place before the destination
|
||||
* - 'after': Place after the destination
|
||||
* - 'insert': Insert into the destination (for blocks)
|
||||
*/
|
||||
export function moveDown(entryId: EntryId, sortableData: EntryId[], entries: RundownEntries) {
|
||||
const nextEntryId = getNextId(entryId, sortableData);
|
||||
export function moveDown(
|
||||
entryId: EntryId,
|
||||
flatOrder: EntryId[],
|
||||
entries: RundownEntries,
|
||||
): { destinationId: EntryId | null; order: 'before' | 'after' | 'insert' } {
|
||||
const currentEntry = entries[entryId];
|
||||
const currentIndex = flatOrder.indexOf(entryId);
|
||||
const nextEntryId = flatOrder[currentIndex + 1];
|
||||
|
||||
// the user is moving down at the end of the list
|
||||
// 1. moving at the top of the list
|
||||
if (!nextEntryId) {
|
||||
return { destinationId: null, order: 'after', isBlock: false };
|
||||
}
|
||||
|
||||
if (nextEntryId.startsWith('end-')) {
|
||||
// move outside the block
|
||||
return { destinationId: nextEntryId.replace('end-', ''), order: 'after', isBlock: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* If the next entry is a block
|
||||
* - 1. blocks need to skip over it
|
||||
* - 2. if the block has children, we insert before the first child
|
||||
* - 3. if the block is empty, we insert into the block
|
||||
*/
|
||||
if (isOntimeBlock(entries[nextEntryId])) {
|
||||
const entry = entries[entryId];
|
||||
|
||||
if (isOntimeBlock(entry)) {
|
||||
// 1. if we are moving a block, we cannot insert it
|
||||
return { destinationId: nextEntryId, order: 'after', isBlock: false };
|
||||
// 1a. we are in a block and need to move outside of it
|
||||
if ('parent' in currentEntry && currentEntry.parent !== null) {
|
||||
return { destinationId: currentEntry.parent, order: 'after' };
|
||||
}
|
||||
// 1b. we are at the end of the rundown, no movement possible
|
||||
return { destinationId: null, order: 'after' };
|
||||
}
|
||||
|
||||
const firstBlockChild = entries[nextEntryId].entries.at(0);
|
||||
if (firstBlockChild) {
|
||||
// 2. add before the first child of the block
|
||||
return { destinationId: firstBlockChild, order: 'before', isBlock: true };
|
||||
} else {
|
||||
// 3. or insert into an empty block
|
||||
return { destinationId: nextEntryId, order: 'insert', isBlock: true };
|
||||
// 2. moving a block (always moves at top level)
|
||||
if (isOntimeBlock(currentEntry)) {
|
||||
// if next entry is inside this block, skip past all children
|
||||
if (currentEntry.entries.includes(nextEntryId)) {
|
||||
const afterBlockIndex = currentIndex + currentEntry.entries.length + 1;
|
||||
const afterBlockId = flatOrder[afterBlockIndex];
|
||||
|
||||
// 2a. block is the last top level entry
|
||||
if (!afterBlockId) {
|
||||
return { destinationId: null, order: 'after' };
|
||||
}
|
||||
// 2b. move after the next top level event
|
||||
return { destinationId: afterBlockId, order: 'after' };
|
||||
}
|
||||
// 2c. empty block move after the next entry
|
||||
return { destinationId: nextEntryId, order: 'after' };
|
||||
}
|
||||
|
||||
const nextEntry = entries[nextEntryId];
|
||||
const currentEntryParent = currentEntry.parent;
|
||||
|
||||
// 3. handle moving relative to blocks
|
||||
if (isOntimeBlock(nextEntry)) {
|
||||
if (currentEntryParent === null) {
|
||||
// we are entering a block
|
||||
if (nextEntry.entries.length === 0) {
|
||||
// 3a. if the block is empty, insert into it
|
||||
return { destinationId: nextEntryId, order: 'insert' };
|
||||
}
|
||||
// 3b. otherwise, add before the first entry in the block
|
||||
const firstBlockEntryId = nextEntry.entries[0];
|
||||
return { destinationId: firstBlockEntryId, order: 'before' };
|
||||
}
|
||||
}
|
||||
|
||||
return { destinationId: nextEntryId, order: 'after', isBlock: Boolean(entries[nextEntryId]?.parent) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function gets the ID if the next entry in the list
|
||||
* returns null if none is found
|
||||
*/
|
||||
function getNextId(entryId: EntryId, sortableData: EntryId[]): EntryId | null {
|
||||
const currentIndex = sortableData.indexOf(entryId);
|
||||
if (currentIndex === -1 || currentIndex === sortableData.length - 1) {
|
||||
// No next ID if not found or at the end
|
||||
return null;
|
||||
// 4. handle moving between block and top level
|
||||
const nextEntryParent = isOntimeEvent(nextEntry) ? nextEntry.parent : null;
|
||||
if (nextEntryParent !== null && currentEntryParent === null) {
|
||||
return { destinationId: nextEntryId, order: 'after' };
|
||||
}
|
||||
return sortableData[currentIndex + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function gets the ID if the previous entry in the list
|
||||
* returns null if none is found
|
||||
*/
|
||||
function getPreviousId(entryId: EntryId, sortableData: EntryId[]): EntryId | null {
|
||||
const currentIndex = sortableData.indexOf(entryId);
|
||||
if (currentIndex < 1) {
|
||||
// No previous ID found or at the beginning
|
||||
return null;
|
||||
}
|
||||
return sortableData[currentIndex - 1];
|
||||
// default - swap positions with next entry
|
||||
return { destinationId: nextEntryId, order: 'after' };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user