mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-17 21:24:11 +00:00
feat: editor layout
This commit is contained in:
committed by
Carlos Valente
parent
967e92e2c8
commit
da02ecd113
@@ -1,25 +1,18 @@
|
||||
import { useCallback } from 'react';
|
||||
import { type EntryId, type OntimeEntry, type Rundown, isOntimeGroup, SupportedEntry } from 'ontime-types';
|
||||
import {
|
||||
getFirstNormal,
|
||||
getLastNormal,
|
||||
getNextGroupNormal,
|
||||
getNextNormal,
|
||||
getPreviousGroupNormal,
|
||||
getPreviousNormal,
|
||||
} from 'ontime-utils';
|
||||
import { type EntryId, type OntimeEntry, type Rundown, SupportedEntry } from 'ontime-types';
|
||||
import { getNextGroupNormal, getNextNormal, getPreviousGroupNormal, getPreviousNormal } from 'ontime-utils';
|
||||
|
||||
import type { useEntryActions } from '../../../common/hooks/useEntryAction';
|
||||
import { useEntryCopy } from '../../../common/stores/entryCopyStore';
|
||||
import { SelectionMode } from '../useEventSelection';
|
||||
|
||||
type SelectionMode = 'shift' | 'click' | 'ctrl';
|
||||
const PAGE_SIZE = 5;
|
||||
|
||||
interface UseRundownCommandsOptions {
|
||||
entries: Rundown['entries'];
|
||||
order: Rundown['order'];
|
||||
flatOrder: EntryId[];
|
||||
entryActions: ReturnType<typeof useEntryActions>;
|
||||
setSelectedEvents: (selection: { id: EntryId; selectMode: SelectionMode; index: number }) => void;
|
||||
selectEntry: (selection: { id: EntryId; selectMode: SelectionMode; index: number }) => void;
|
||||
handleCollapseGroup: (collapsed: boolean, groupId: EntryId) => void;
|
||||
}
|
||||
|
||||
@@ -28,9 +21,9 @@ interface UseRundownCommandsOptions {
|
||||
*/
|
||||
export function useRundownCommands({
|
||||
entries,
|
||||
order,
|
||||
flatOrder,
|
||||
entryActions,
|
||||
setSelectedEvents,
|
||||
selectEntry: applySelection,
|
||||
handleCollapseGroup,
|
||||
}: UseRundownCommandsOptions) {
|
||||
const { addEntry, clone, deleteEntry, move, reorderEntry } = entryActions;
|
||||
@@ -38,13 +31,13 @@ export function useRundownCommands({
|
||||
const deleteAtCursor = useCallback(
|
||||
(cursor: string | null) => {
|
||||
if (!cursor) return;
|
||||
const { entry, index } = getPreviousNormal(entries, order, cursor);
|
||||
const { entry, index } = getPreviousNormal(entries, flatOrder, cursor);
|
||||
deleteEntry([cursor]);
|
||||
if (entry && index !== null) {
|
||||
setSelectedEvents({ id: entry.id, selectMode: 'click', index });
|
||||
applySelection({ id: entry.id, selectMode: 'click', index });
|
||||
}
|
||||
},
|
||||
[entries, order, deleteEntry, setSelectedEvents],
|
||||
[entries, flatOrder, deleteEntry, applySelection],
|
||||
);
|
||||
|
||||
const insertCopyAtId = useCallback(
|
||||
@@ -67,7 +60,7 @@ export function useRundownCommands({
|
||||
|
||||
if (entryCopyMode === 'cut') {
|
||||
if (!normalisedAtId) {
|
||||
const firstId = order[0];
|
||||
const firstId = flatOrder[0];
|
||||
if (!firstId || firstId === entryCopyId) {
|
||||
return;
|
||||
}
|
||||
@@ -92,7 +85,7 @@ export function useRundownCommands({
|
||||
before: above ? normalisedAtId ?? undefined : undefined,
|
||||
});
|
||||
},
|
||||
[entries, order, clone, reorderEntry],
|
||||
[entries, flatOrder, clone, reorderEntry],
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -111,70 +104,39 @@ export function useRundownCommands({
|
||||
|
||||
const selectGroup = useCallback(
|
||||
(cursor: EntryId | null, direction: 'up' | 'down') => {
|
||||
if (order.length < 1) {
|
||||
if (flatOrder.length < 1) {
|
||||
return null;
|
||||
}
|
||||
let newCursor = cursor;
|
||||
if (cursor === null) {
|
||||
// there is no cursor, we select the first or last depending on direction
|
||||
const selected = direction === 'up' ? getLastNormal(entries, order) : getFirstNormal(entries, order);
|
||||
|
||||
if (isOntimeGroup(selected)) {
|
||||
setSelectedEvents({ id: selected.id, selectMode: 'click', index: direction === 'up' ? order.length : 0 });
|
||||
return selected.id;
|
||||
}
|
||||
newCursor = selected?.id ?? null;
|
||||
}
|
||||
|
||||
if (newCursor === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// otherwise we select the next or previous
|
||||
const selected =
|
||||
direction === 'up'
|
||||
? getPreviousGroupNormal(entries, order, newCursor)
|
||||
: getNextGroupNormal(entries, order, newCursor);
|
||||
? getPreviousGroupNormal(entries, flatOrder, cursor)
|
||||
: getNextGroupNormal(entries, flatOrder, cursor);
|
||||
|
||||
if (selected.entry !== null && selected.index !== null) {
|
||||
setSelectedEvents({ id: selected.entry.id, selectMode: 'click', index: selected.index });
|
||||
if (selected.entry && selected.index !== null) {
|
||||
applySelection({ id: selected.entry.id, selectMode: 'click', index: selected.index });
|
||||
return selected.entry.id;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
[order, entries, setSelectedEvents],
|
||||
[flatOrder, entries, applySelection],
|
||||
);
|
||||
|
||||
/**
|
||||
* TODO: getPreviousNormal and getNextNormal do not work across group boundaries
|
||||
*/
|
||||
const selectEntry = useCallback(
|
||||
(cursor: EntryId | null, direction: 'up' | 'down') => {
|
||||
if (order.length < 1) {
|
||||
if (flatOrder.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cursor === null) {
|
||||
// there is no cursor, we select the first or last depending on direction if it exists
|
||||
const selected = direction === 'up' ? getLastNormal(entries, order) : getFirstNormal(entries, order);
|
||||
if (selected !== null) {
|
||||
setSelectedEvents({ id: selected.id, selectMode: 'click', index: direction === 'up' ? order.length : 0 });
|
||||
return selected.id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// otherwise we select the next or previous
|
||||
const selected =
|
||||
direction === 'up' ? getPreviousNormal(entries, order, cursor) : getNextNormal(entries, order, cursor);
|
||||
direction === 'up' ? getPreviousNormal(entries, flatOrder, cursor) : getNextNormal(entries, flatOrder, cursor);
|
||||
|
||||
if (selected.entry !== null && selected.index !== null) {
|
||||
setSelectedEvents({ id: selected.entry.id, selectMode: 'click', index: selected.index });
|
||||
if (selected.entry && selected.index !== null) {
|
||||
applySelection({ id: selected.entry.id, selectMode: 'click', index: selected.index });
|
||||
return selected.entry.id;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
[order, entries, setSelectedEvents],
|
||||
[flatOrder, entries, applySelection],
|
||||
);
|
||||
|
||||
const moveEntry = useCallback(
|
||||
@@ -204,67 +166,52 @@ export function useRundownCommands({
|
||||
|
||||
const selectEdge = useCallback(
|
||||
(direction: 'top' | 'bottom') => {
|
||||
if (order.length < 1) {
|
||||
if (flatOrder.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const selected = direction === 'top' ? getFirstNormal(entries, order) : getLastNormal(entries, order);
|
||||
if (!selected) {
|
||||
return null;
|
||||
}
|
||||
const index = direction === 'top' ? 0 : flatOrder.length - 1;
|
||||
const selectedId = flatOrder[index];
|
||||
if (!selectedId) return null;
|
||||
|
||||
const index = order.indexOf(selected.id);
|
||||
if (index === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
setSelectedEvents({ id: selected.id, selectMode: 'click', index });
|
||||
return selected.id;
|
||||
applySelection({ id: selectedId, selectMode: 'click', index });
|
||||
return selectedId;
|
||||
},
|
||||
[entries, order, setSelectedEvents],
|
||||
[flatOrder, applySelection],
|
||||
);
|
||||
|
||||
const selectPage = useCallback(
|
||||
(cursor: EntryId | null, direction: 'up' | 'down') => {
|
||||
if (order.length < 1) {
|
||||
if (flatOrder.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cursor === null) {
|
||||
const selected = direction === 'down' ? getFirstNormal(entries, order) : getLastNormal(entries, order);
|
||||
if (!selected) {
|
||||
return null;
|
||||
}
|
||||
const index = order.indexOf(selected.id);
|
||||
if (index !== -1) {
|
||||
setSelectedEvents({ id: selected.id, selectMode: 'click', index });
|
||||
return selected.id;
|
||||
}
|
||||
return null;
|
||||
let currentIndex = cursor ? flatOrder.indexOf(cursor) : -1;
|
||||
if (currentIndex === -1) {
|
||||
currentIndex = direction === 'down' ? -1 : flatOrder.length;
|
||||
}
|
||||
|
||||
let nextCursor = cursor;
|
||||
let target: { entry: OntimeEntry | null; index: number | null } | null = null;
|
||||
|
||||
let targetIndex = currentIndex;
|
||||
let lastValidIndex: number | null = null;
|
||||
for (let step = 0; step < PAGE_SIZE; step += 1) {
|
||||
const next =
|
||||
direction === 'down'
|
||||
? getNextNormal(entries, order, nextCursor)
|
||||
: getPreviousNormal(entries, order, nextCursor);
|
||||
if (next.entry === null || next.index === null) {
|
||||
targetIndex = direction === 'down' ? targetIndex + 1 : targetIndex - 1;
|
||||
if (targetIndex < 0 || targetIndex >= flatOrder.length) {
|
||||
break;
|
||||
}
|
||||
target = next;
|
||||
nextCursor = next.entry.id;
|
||||
lastValidIndex = targetIndex;
|
||||
}
|
||||
|
||||
if (target?.entry && target.index !== null) {
|
||||
setSelectedEvents({ id: target.entry.id, selectMode: 'click', index: target.index });
|
||||
return target.entry.id;
|
||||
if (lastValidIndex === null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
||||
const selectedId = flatOrder[lastValidIndex];
|
||||
if (!selectedId) return null;
|
||||
|
||||
applySelection({ id: selectedId, selectMode: 'click', index: lastValidIndex });
|
||||
return selectedId;
|
||||
},
|
||||
[entries, order, setSelectedEvents],
|
||||
[flatOrder, applySelection],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -11,7 +11,7 @@ interface UseRundownDndOptions {
|
||||
sortableData: EntryId[];
|
||||
setSortableData: Dispatch<SetStateAction<EntryId[]>>;
|
||||
getIsCollapsed: (groupId: EntryId) => boolean;
|
||||
handleCollapseGroup: (collapsed: boolean, groupId: EntryId) => void;
|
||||
handleCollapseGroup: (collapsed: boolean, groupId: EntryId | undefined) => void;
|
||||
entryActions: ReturnType<typeof useEntryActions>;
|
||||
}
|
||||
|
||||
@@ -132,13 +132,11 @@ export function useRundownDnd({
|
||||
if (event.over?.data.current?.type !== SupportedEntry.Group) {
|
||||
return;
|
||||
}
|
||||
const groupId = event.over?.id as EntryId;
|
||||
const isCollapsed = getIsCollapsed(groupId);
|
||||
if (isCollapsed) {
|
||||
handleCollapseGroup(false, groupId);
|
||||
}
|
||||
|
||||
const groupId = event.over?.id as EntryId | undefined;
|
||||
handleCollapseGroup(false, groupId);
|
||||
},
|
||||
[getIsCollapsed, handleCollapseGroup],
|
||||
[handleCollapseGroup],
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
|
||||
Reference in New Issue
Block a user