refactor: table mode and context

This commit is contained in:
Carlos Valente
2026-01-10 09:41:40 +01:00
committed by Carlos Valente
parent 8c22968bc9
commit c5045ad82e
64 changed files with 560 additions and 273 deletions
@@ -1,4 +1,4 @@
import { MouseEvent, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { MouseEvent, useEffect, useRef } from 'react';
import {
IoAdd,
IoDuplicateOutline,
@@ -15,9 +15,9 @@ import { CSS } from '@dnd-kit/utilities';
import { EndAction, EntryId, Playback, TimerType, TimeStrategy } from 'ontime-types';
import { isPlaybackActive } from 'ontime-utils';
import { useEntryActionsContext } from '../../../common/context/EntryActionsContext';
import { useContextMenu } from '../../../common/hooks/useContextMenu';
import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
import { useRundownEntryActions } from '../context/RundownActionsContext';
import { useEventIdSwapping } from '../useEventIdSwapping';
import { getSelectionMode, useEventSelection } from '../useEventSelection';
@@ -97,7 +97,7 @@ export default function RundownEvent({
const setSelectedEventId = useEventIdSwapping((state) => state.setSelectedEventId);
const clearSelectedEventId = useEventIdSwapping((state) => state.clearSelectedEventId);
const { updateEntry, batchUpdateEvents, clone, deleteEntry, groupEntries, swapEvents } = useRundownEntryActions();
const { updateEntry, batchUpdateEvents, clone, deleteEntry, groupEntries, swapEvents } = useEntryActionsContext();
const isSelected = useEventSelection((state) => state.selectedEvents.has(eventId));
const unselect = useEventSelection((state) => state.unselect);
@@ -107,7 +107,6 @@ export default function RundownEvent({
const selectedEvents = useEventSelection((state) => state.selectedEvents);
const handleRef = useRef<null | HTMLSpanElement>(null);
const [isVisible, setIsVisible] = useState(false);
const [onContextMenu] = useContextMenu<HTMLDivElement>(() =>
selectedEvents.size > 1
@@ -236,31 +235,6 @@ export default function RundownEvent({
}
}, [hasCursor]);
useLayoutEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
}
},
{
root: null,
threshold: 1,
},
);
const handleRefCurrent = handleRef.current;
if (handleRefCurrent) {
observer.observe(handleRefCurrent);
}
return () => {
if (handleRefCurrent) {
observer.unobserve(handleRefCurrent);
}
};
}, [handleRef]);
const blockClasses = cx([
style.rundownEvent,
skip ? style.skip : null,
@@ -308,33 +282,31 @@ export default function RundownEvent({
<span className={style.cue}>{cue}</span>
</div>
{isVisible && (
<RundownEventInner
timeStart={timeStart}
timeEnd={timeEnd}
duration={duration}
linkStart={linkStart}
countToEnd={countToEnd}
timeStrategy={timeStrategy}
eventId={eventId}
eventIndex={eventIndex}
endAction={endAction}
timerType={timerType}
title={title}
note={note}
delay={delay}
isNext={isNext}
skip={skip}
loaded={loaded}
playback={playback}
isRolling={isRolling}
dayOffset={dayOffset}
isPast={isPast}
totalGap={totalGap}
isLinkedToLoaded={isLinkedToLoaded}
hasTriggers={hasTriggers}
/>
)}
<RundownEventInner
timeStart={timeStart}
timeEnd={timeEnd}
duration={duration}
linkStart={linkStart}
countToEnd={countToEnd}
timeStrategy={timeStrategy}
eventId={eventId}
eventIndex={eventIndex}
endAction={endAction}
timerType={timerType}
title={title}
note={note}
delay={delay}
isNext={isNext}
skip={skip}
loaded={loaded}
playback={playback}
isRolling={isRolling}
dayOffset={dayOffset}
isPast={isPast}
totalGap={totalGap}
isLinkedToLoaded={isLinkedToLoaded}
hasTriggers={hasTriggers}
/>
</div>
);
}
@@ -1,4 +1,4 @@
import { memo, useEffect, useState } from 'react';
import { memo } from 'react';
import {
IoArrowDown,
IoArrowUp,
@@ -76,17 +76,11 @@ function RundownEventInner({
isLinkedToLoaded,
hasTriggers,
}: RundownEventInnerProps) {
const [renderInner, setRenderInner] = useState(false);
const [editorMode] = useSessionStorage({
key: sessionKeys.editorMode,
defaultValue: AppMode.Edit,
});
useEffect(() => {
setRenderInner(true);
}, []);
const eventIsPlaying = playback === Playback.Play;
const eventIsPaused = playback === Playback.Pause;
@@ -97,7 +91,7 @@ function RundownEventInner({
playBtnStyles._hover = {};
}
return !renderInner ? null : (
return (
<>
<div className={cx([style.eventTimers, editorMode === AppMode.Edit && style.editMode])}>
<TimeInputFlow
@@ -161,8 +155,7 @@ function RundownEventInner({
);
}
function EndActionIcon(props: { action: EndAction; className: string }) {
const { action, className } = props;
function EndActionIcon({ action, className }: { action: EndAction; className: string }) {
const maybeActiveClasses = cx([action !== EndAction.None && style.active, className]);
if (action === EndAction.LoadNext) {
@@ -174,8 +167,7 @@ function EndActionIcon(props: { action: EndAction; className: string }) {
return <IoPlay className={className} />;
}
function TimerIcon(props: { type: TimerType; className: string }) {
const { type, className } = props;
function TimerIcon({ type, className }: { type: TimerType; className: string }) {
if (type === TimerType.CountUp) {
return <IoArrowUp className={className} />;
}
@@ -3,8 +3,8 @@ import { IoPause, IoPlay, IoReload, IoRemoveCircle, IoRemoveCircleOutline } from
import IconButton from '../../../../common/components/buttons/IconButton';
import Tooltip from '../../../../common/components/tooltip/Tooltip';
import { useEntryActionsContext } from '../../../../common/context/EntryActionsContext';
import { setEventPlayback } from '../../../../common/hooks/useSocket';
import { useRundownEntryActions } from '../../context/RundownActionsContext';
import style from '../RundownEvent.module.scss';
@@ -26,7 +26,7 @@ function RundownEventPlayback({
loaded,
disablePlayback,
}: RundownEventPlaybackProps) {
const { updateEntry } = useRundownEntryActions();
const { updateEntry } = useEntryActionsContext();
const toggleSkip = (event: MouseEvent) => {
event.stopPropagation();