mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 19:33:46 +00:00
feat: edit mode (#344)
* style: add mode selection to menu * style: rename rundown functions * feat: edit mode
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export enum AppMode {
|
||||
Run = 'run',
|
||||
Edit = 'edit',
|
||||
}
|
||||
|
||||
type AppModeStore = {
|
||||
mode: AppMode;
|
||||
cursor: string | null;
|
||||
editId: string | null;
|
||||
setMode: (mode: AppMode) => void;
|
||||
setCursor: (id: string | null, isEditable?: boolean) => void;
|
||||
setEditId: (id: string | null) => void;
|
||||
};
|
||||
|
||||
export const useAppMode = create<AppModeStore>()((set) => ({
|
||||
mode: AppMode.Edit,
|
||||
cursor: null,
|
||||
editId: null,
|
||||
setMode: (mode: AppMode) =>
|
||||
set((state) => {
|
||||
return mode === AppMode.Edit
|
||||
? {
|
||||
editId: state.cursor,
|
||||
mode: mode,
|
||||
}
|
||||
: {
|
||||
editId: null,
|
||||
mode: mode,
|
||||
};
|
||||
}),
|
||||
setCursor: (id: string | null, isEditable?: boolean) =>
|
||||
set((state) => {
|
||||
if (isEditable) {
|
||||
return state.mode === AppMode.Edit
|
||||
? {
|
||||
cursor: id,
|
||||
editId: id,
|
||||
}
|
||||
: {
|
||||
cursor: id,
|
||||
};
|
||||
} else {
|
||||
return { cursor: id, editId: null };
|
||||
}
|
||||
}),
|
||||
setEditId: (id: string | null) =>
|
||||
set((state) => {
|
||||
return state.mode === AppMode.Edit
|
||||
? {
|
||||
cursor: id,
|
||||
editId: id,
|
||||
}
|
||||
: {
|
||||
editId: id,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
@@ -1,24 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { booleanFromLocalStorage } from '../utils/localStorage';
|
||||
|
||||
type CursorStore = {
|
||||
cursor: number;
|
||||
isCursorLocked: boolean;
|
||||
toggleCursorLocked: (newValue?: boolean) => void;
|
||||
moveCursorTo: (index: number) => void;
|
||||
};
|
||||
|
||||
const cursorLockedKey = 'ontime-cursor-islocked';
|
||||
|
||||
export const useCursor = create<CursorStore>()((set) => ({
|
||||
cursor: 0,
|
||||
isCursorLocked: booleanFromLocalStorage(cursorLockedKey, false),
|
||||
toggleCursorLocked: (newValue?: boolean) =>
|
||||
set((state) => {
|
||||
const val = typeof newValue === 'undefined' ? !state.isCursorLocked : newValue;
|
||||
localStorage.setItem(cursorLockedKey, String(val));
|
||||
return { isCursorLocked: val };
|
||||
}),
|
||||
moveCursorTo: (index: number) => set(() => ({ cursor: index })),
|
||||
}));
|
||||
@@ -1,13 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
type EventEditorStore = {
|
||||
openId: string | null;
|
||||
setOpenEvent: (eventId: string) => void;
|
||||
removeOpenEvent: () => void;
|
||||
};
|
||||
|
||||
export const useEventEditorStore = create<EventEditorStore>()((set) => ({
|
||||
openId: null,
|
||||
setOpenEvent: (eventId: string | null) => set({ openId: eventId }),
|
||||
removeOpenEvent: () => set({ openId: null }),
|
||||
}));
|
||||
@@ -1,4 +1,4 @@
|
||||
import { formatEventList, getEventsWithDelay, trimEventlist } from '../eventsManager';
|
||||
import { formatEventList, getEventsWithDelay, trimRundown } from '../eventsManager';
|
||||
|
||||
describe('getEventsWithDelay function', () => {
|
||||
test('with positive delays', () => {
|
||||
@@ -365,7 +365,7 @@ describe('test trimEventlist function', () => {
|
||||
{ id: '8' },
|
||||
];
|
||||
|
||||
const l = trimEventlist(testData, selectedId, limit);
|
||||
const l = trimRundown(testData, selectedId, limit);
|
||||
expect(l.length).toBe(limit);
|
||||
expect(l).toStrictEqual(expected);
|
||||
});
|
||||
@@ -383,7 +383,7 @@ describe('test trimEventlist function', () => {
|
||||
{ id: '8' },
|
||||
];
|
||||
|
||||
const l = trimEventlist(testData, selectedId, limit);
|
||||
const l = trimRundown(testData, selectedId, limit);
|
||||
expect(l.length).toBe(limit);
|
||||
expect(l).toStrictEqual(expected);
|
||||
});
|
||||
@@ -401,7 +401,7 @@ describe('test trimEventlist function', () => {
|
||||
{ id: '9' },
|
||||
];
|
||||
|
||||
const l = trimEventlist(testData, selectedId, limit);
|
||||
const l = trimRundown(testData, selectedId, limit);
|
||||
expect(l.length).toBe(limit);
|
||||
expect(l).toStrictEqual(expected);
|
||||
});
|
||||
@@ -419,7 +419,7 @@ describe('test trimEventlist function', () => {
|
||||
{ id: '8' },
|
||||
];
|
||||
|
||||
const l = trimEventlist(testData, selectedId, limit);
|
||||
const l = trimRundown(testData, selectedId, limit);
|
||||
expect(l.length).toBe(limit);
|
||||
expect(l).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
@@ -4,18 +4,18 @@ import { formatTime } from './time';
|
||||
|
||||
/**
|
||||
* @description From a list of events, returns only events of type event with calculated delays
|
||||
* @param {Object[]} events - given events
|
||||
* @param {Object[]} rundown - given rundown
|
||||
* @returns {Object[]} Filtered events with calculated delays
|
||||
*/
|
||||
|
||||
export const getEventsWithDelay = (events: OntimeRundownEntry[]): OntimeEvent[] => {
|
||||
if (events == null) return [];
|
||||
export const getEventsWithDelay = (rundown: OntimeRundownEntry[]): OntimeEvent[] => {
|
||||
if (rundown == null) return [];
|
||||
|
||||
const delayedEvents: OntimeEvent[] = [];
|
||||
|
||||
// Add running delay
|
||||
let delay = 0;
|
||||
for (const event of events) {
|
||||
for (const event of rundown) {
|
||||
if (event.type === SupportedEvent.Block) delay = 0;
|
||||
else if (event.type === SupportedEvent.Delay) {
|
||||
if (typeof event.duration === 'number') {
|
||||
@@ -36,29 +36,29 @@ export const getEventsWithDelay = (events: OntimeRundownEntry[]): OntimeEvent[]
|
||||
|
||||
/**
|
||||
* @description Returns trimmed event list array
|
||||
* @param {Object[]} events - given events
|
||||
* @param {Object[]} rundown - given rundown
|
||||
* @param {string} selectedId - id of currently selected event
|
||||
* @param {number} limit - max number of events to return
|
||||
* @returns {Object[]} Event list with maximum <limit> objects
|
||||
*/
|
||||
export const trimEventlist = (events: OntimeRundownEntry[], selectedId: string, limit: number) => {
|
||||
if (events == null) return [];
|
||||
export const trimRundown = (rundown: OntimeRundownEntry[], selectedId: string, limit: number) => {
|
||||
if (rundown == null) return [];
|
||||
|
||||
const BEFORE = 2;
|
||||
const trimmedEvents = [...events];
|
||||
const trimmedRundown = [...rundown];
|
||||
|
||||
// limit events length if necessary
|
||||
if (limit != null) {
|
||||
while (trimmedEvents.length > limit) {
|
||||
const idx = trimmedEvents.findIndex((e) => e.id === selectedId);
|
||||
while (trimmedRundown.length > limit) {
|
||||
const idx = trimmedRundown.findIndex((e) => e.id === selectedId);
|
||||
if (idx <= BEFORE) {
|
||||
trimmedEvents.pop();
|
||||
trimmedRundown.pop();
|
||||
} else {
|
||||
trimmedEvents.shift();
|
||||
trimmedRundown.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
return trimmedEvents;
|
||||
return trimmedRundown;
|
||||
};
|
||||
|
||||
type FormatEventListOptionsProp = {
|
||||
@@ -66,7 +66,7 @@ type FormatEventListOptionsProp = {
|
||||
};
|
||||
/**
|
||||
* @description Returns list of events formatted to be displayed
|
||||
* @param {Object[]} events - given events
|
||||
* @param {Object[]} rundown - given rundown
|
||||
* @param {string} selectedId - id of currently selected event
|
||||
* @param {string} nextId - id of next event
|
||||
* @param {object} [options]
|
||||
@@ -74,15 +74,15 @@ type FormatEventListOptionsProp = {
|
||||
* @returns {Object[]} Formatted list of events [{time: -, title: -, isNow, isNext}]
|
||||
*/
|
||||
export const formatEventList = (
|
||||
events: OntimeEvent[],
|
||||
rundown: OntimeEvent[],
|
||||
selectedId: string,
|
||||
nextId: string,
|
||||
options: FormatEventListOptionsProp,
|
||||
) => {
|
||||
if (events == null) return [];
|
||||
if (rundown == null) return [];
|
||||
const { showEnd = false } = options;
|
||||
|
||||
const givenEvents = [...events];
|
||||
const givenEvents = [...rundown];
|
||||
|
||||
// format list
|
||||
const formattedEvents = [];
|
||||
@@ -124,3 +124,42 @@ export const cloneEvent = (event: OntimeEvent, after?: string): ClonedEvent => {
|
||||
after: after,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets first event in rundown, if it exists
|
||||
* @param {OntimeRundownEntry[]} rundown
|
||||
* @return {OntimeEvent | null}
|
||||
*/
|
||||
export function getFirstEvent(rundown: OntimeRundownEntry[]) {
|
||||
return rundown.length ? rundown[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets next event in rundown, if it exists
|
||||
* @param {OntimeRundownEntry[]} rundown
|
||||
* @param {string} currentId
|
||||
* @return {OntimeEvent | null}
|
||||
*/
|
||||
export function getNextEvent(rundown: OntimeRundownEntry[], currentId: string) {
|
||||
const index = rundown.findIndex((event) => event.id === currentId);
|
||||
if (index !== -1 && index + 1 < rundown.length) {
|
||||
return rundown[index + 1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets previous event in rundown, if it exists
|
||||
* @param {OntimeRundownEntry[]} rundown
|
||||
* @param {string} currentId
|
||||
* @return {OntimeEvent | null}
|
||||
*/
|
||||
export function getPreviousEvent(rundown: OntimeRundownEntry[], currentId: string) {
|
||||
const index = rundown.findIndex((event) => event.id === currentId);
|
||||
if (index !== -1 && index - 1 >= 0) {
|
||||
return rundown[index - 1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user