mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-05 14:29:20 +00:00
feat: multiple selection (#703)
feat: multiple event selection --------- Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com> Co-authored-by: Alex <ac@omnivox.dk>
This commit is contained in:
@@ -3,28 +3,44 @@ import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
|
||||
import { IoCopyOutline } from '@react-icons/all-files/io5/IoCopyOutline';
|
||||
import { IoPeople } from '@react-icons/all-files/io5/IoPeople';
|
||||
import { IoPeopleOutline } from '@react-icons/all-files/io5/IoPeopleOutline';
|
||||
import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo';
|
||||
import { IoSwapVertical } from '@react-icons/all-files/io5/IoSwapVertical';
|
||||
import { EndAction, OntimeEvent, Playback, TimerType } from 'ontime-types';
|
||||
|
||||
import { useContextMenu } from '../../../common/hooks/useContextMenu';
|
||||
import { useAppMode } from '../../../common/stores/appModeStore';
|
||||
import useRundown from '../../../common/hooks-query/useRundown';
|
||||
import copyToClipboard from '../../../common/utils/copyToClipboard';
|
||||
import { isMacOS } from '../../../common/utils/deviceUtils';
|
||||
import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
|
||||
import type { EventItemActions } from '../RundownEntry';
|
||||
import { useEventIdSwapping } from '../useEventIdSwapping';
|
||||
import { EditMode, useEventSelection } from '../useEventSelection';
|
||||
|
||||
import EventBlockInner from './EventBlockInner';
|
||||
|
||||
import style from './EventBlock.module.scss';
|
||||
|
||||
const getEditMode = (event: MouseEvent): EditMode => {
|
||||
if ((isMacOS() && event.metaKey) || event.ctrlKey) {
|
||||
return 'ctrl';
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
return 'shift';
|
||||
}
|
||||
|
||||
return 'click';
|
||||
};
|
||||
|
||||
interface EventBlockProps {
|
||||
cue: string;
|
||||
timeStart: number;
|
||||
timeEnd: number;
|
||||
duration: number;
|
||||
eventId: string;
|
||||
eventIndex: number;
|
||||
isPublic: boolean;
|
||||
endAction: EndAction;
|
||||
timerType: TimerType;
|
||||
@@ -61,6 +77,7 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
timeEnd,
|
||||
duration,
|
||||
isPublic = true,
|
||||
eventIndex,
|
||||
endAction,
|
||||
timerType,
|
||||
title,
|
||||
@@ -80,37 +97,66 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
isFirstEvent,
|
||||
} = props;
|
||||
const { selectedEventId, setSelectedEventId, clearSelectedEventId } = useEventIdSwapping();
|
||||
const moveCursorTo = useAppMode((state) => state.setCursor);
|
||||
const { selectedEvents, setSelectedEvents } = useEventSelection();
|
||||
const { data: rundown = [] } = useRundown();
|
||||
const handleRef = useRef<null | HTMLSpanElement>(null);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const openId = useAppMode((state) => state.editId);
|
||||
const [onContextMenu] = useContextMenu<HTMLDivElement>([
|
||||
{ label: `Copy ID: ${eventId}`, icon: IoCopyOutline, onClick: () => copyToClipboard(eventId) },
|
||||
{
|
||||
label: 'Toggle public',
|
||||
icon: IoPeopleOutline,
|
||||
onClick: () =>
|
||||
actionHandler('update', {
|
||||
field: 'isPublic',
|
||||
value: !isPublic,
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Add to swap',
|
||||
icon: IoAdd,
|
||||
onClick: () => setSelectedEventId(eventId),
|
||||
withDivider: true,
|
||||
},
|
||||
{
|
||||
label: `Swap this event with ${selectedEventId ?? ''}`,
|
||||
icon: IoSwapVertical,
|
||||
onClick: () => {
|
||||
actionHandler('swap', { field: 'id', value: selectedEventId });
|
||||
clearSelectedEventId();
|
||||
},
|
||||
isDisabled: selectedEventId == null || selectedEventId === eventId,
|
||||
},
|
||||
]);
|
||||
|
||||
const [onContextMenu] = useContextMenu<HTMLDivElement>(
|
||||
selectedEvents.size > 1
|
||||
? [
|
||||
{
|
||||
label: 'Visiblity',
|
||||
group: [
|
||||
{
|
||||
label: 'Make public',
|
||||
icon: IoPeople,
|
||||
onClick: () =>
|
||||
actionHandler('update', {
|
||||
field: 'isPublic',
|
||||
value: true,
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Make private',
|
||||
icon: IoPeopleOutline,
|
||||
onClick: () =>
|
||||
actionHandler('update', {
|
||||
field: 'isPublic',
|
||||
value: false,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [
|
||||
{ label: `Copy ID: ${eventId}`, icon: IoCopyOutline, onClick: () => copyToClipboard(eventId) },
|
||||
{
|
||||
label: 'Toggle public',
|
||||
icon: IoPeopleOutline,
|
||||
onClick: () =>
|
||||
actionHandler('update', {
|
||||
field: 'isPublic',
|
||||
value: !isPublic,
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Add to swap',
|
||||
icon: IoAdd,
|
||||
onClick: () => setSelectedEventId(eventId),
|
||||
withDivider: true,
|
||||
},
|
||||
{
|
||||
label: `Swap this event with ${selectedEventId ?? ''}`,
|
||||
icon: IoSwapVertical,
|
||||
onClick: () => {
|
||||
actionHandler('swap', { field: 'id', value: selectedEventId });
|
||||
clearSelectedEventId();
|
||||
},
|
||||
isDisabled: selectedEventId == null || selectedEventId === eventId,
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const {
|
||||
isDragging,
|
||||
@@ -179,12 +225,23 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
isPast ? style.past : null,
|
||||
selected ? style.selected : null,
|
||||
playback ? style[playback] : null,
|
||||
hasCursor ? style.hasCursor : null,
|
||||
selectedEvents.has(eventId) ? style.hasCursor : null,
|
||||
]);
|
||||
|
||||
const handleFocusClick = (event: MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
moveCursorTo(eventId, true);
|
||||
|
||||
// event.button === 2 is a right-click
|
||||
// disable selection if the user selected events and right clicks
|
||||
// so the context menu shows up
|
||||
if (selectedEvents.size > 1 && event.button === 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const editMode = getEditMode(event);
|
||||
return setSelectedEvents({ id: eventId, index: eventIndex, rundown, editMode });
|
||||
|
||||
// moveCursorTo(eventId, true);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -192,7 +249,7 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
className={blockClasses}
|
||||
ref={setNodeRef}
|
||||
style={dragStyle}
|
||||
onClick={handleFocusClick}
|
||||
onMouseDown={handleFocusClick}
|
||||
onContextMenu={onContextMenu}
|
||||
id='event-block'
|
||||
>
|
||||
@@ -204,11 +261,11 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
</div>
|
||||
{isVisible && (
|
||||
<EventBlockInner
|
||||
isOpen={openId === eventId}
|
||||
timeStart={timeStart}
|
||||
timeEnd={timeEnd}
|
||||
duration={duration}
|
||||
eventId={eventId}
|
||||
eventIndex={eventIndex}
|
||||
isPublic={isPublic}
|
||||
endAction={endAction}
|
||||
timerType={timerType}
|
||||
|
||||
Reference in New Issue
Block a user