) {
>
{showActionMenu && (
|
-
+ }
+ variant='ontime-subtle'
+ onClick={(event) => {
+ const rect = event.currentTarget.getBoundingClientRect();
+ const yPos = 8 + rect.y + rect.height / 2;
+ openMenu({ x: rect.x, y: yPos }, eventId, rowIndex);
+ }}
+ />
|
)}
{!hideIndexColumn && (
diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/CuesheetTableMenu.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/CuesheetTableMenu.tsx
index ffa94caac..4d6c2055c 100644
--- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/CuesheetTableMenu.tsx
+++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/CuesheetTableMenu.tsx
@@ -1,70 +1,35 @@
-import { MenuDivider, MenuItem, MenuList, Portal } from '@chakra-ui/react';
-import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
-import { IoArrowDown } from '@react-icons/all-files/io5/IoArrowDown';
-import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
-import { IoDuplicateOutline } from '@react-icons/all-files/io5/IoDuplicateOutline';
-import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
-import { IoTrash } from '@react-icons/all-files/io5/IoTrash';
-import { isOntimeEvent, SupportedEvent } from 'ontime-types';
+import { memo } from 'react';
+import { Menu, MenuButton, Portal } from '@chakra-ui/react';
-import { useEventAction } from '../../../../common/hooks/useEventAction';
-import { cloneEvent } from '../../../../common/utils/eventsManager';
+import CuesheetTableMenuActionsProps from './CuesheetTableMenuActions';
+import { useCuesheetTableMenu } from './useCuesheetTableMenu';
interface CuesheetTableMenuProps {
- eventId: string;
- entryIndex: number;
- showModal: (entryId: string) => void;
+ showModal: (eventId: string) => void;
}
-export default function CuesheetTableMenu(props: CuesheetTableMenuProps) {
- const { eventId, entryIndex, showModal } = props;
- const { addEvent, getEventById, reorderEvent, deleteEvent } = useEventAction();
+export default memo(CuesheetTableMenu);
- const handleCloneEvent = () => {
- const currentEvent = getEventById(eventId);
- if (!currentEvent || !isOntimeEvent(currentEvent)) {
- return;
- }
-
- const newEvent = cloneEvent(currentEvent);
- try {
- addEvent(newEvent, { after: eventId });
- } catch (_error) {
- // we do not handle errors here
- }
- };
+function CuesheetTableMenu(props: CuesheetTableMenuProps) {
+ const { showModal } = props;
+ const { isOpen, eventId, entryIndex, position, closeMenu } = useCuesheetTableMenu();
return (
-
- } onClick={() => showModal(eventId)}>
- Edit ...
-
-
- } onClick={() => addEvent({ type: SupportedEvent.Event }, { before: eventId })}>
- Add event above
-
- } onClick={() => addEvent({ type: SupportedEvent.Event }, { after: eventId })}>
- Add event below
-
- } onClick={handleCloneEvent}>
- Clone event
-
-
- }
- onClick={() => reorderEvent(eventId, entryIndex, entryIndex - 1)}
- >
- Move up
-
- } onClick={() => reorderEvent(eventId, entryIndex, entryIndex + 1)}>
- Move down
-
- } onClick={() => deleteEvent([eventId])}>
- Delete
-
-
+ {isOpen && (
+
+ )}
);
}
diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/CuesheetTableMenuActions.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/CuesheetTableMenuActions.tsx
new file mode 100644
index 000000000..2932de3d8
--- /dev/null
+++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/CuesheetTableMenuActions.tsx
@@ -0,0 +1,68 @@
+import { MenuDivider, MenuItem, MenuList } from '@chakra-ui/react';
+import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
+import { IoArrowDown } from '@react-icons/all-files/io5/IoArrowDown';
+import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
+import { IoDuplicateOutline } from '@react-icons/all-files/io5/IoDuplicateOutline';
+import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
+import { IoTrash } from '@react-icons/all-files/io5/IoTrash';
+import { isOntimeEvent, SupportedEvent } from 'ontime-types';
+
+import { useEventAction } from '../../../../common/hooks/useEventAction';
+import { cloneEvent } from '../../../../common/utils/eventsManager';
+
+interface CuesheetTableMenuActionsProps {
+ eventId: string;
+ entryIndex: number;
+ showModal: (entryId: string) => void;
+}
+
+export default function CuesheetTableMenuActions(props: CuesheetTableMenuActionsProps) {
+ const { eventId, entryIndex, showModal } = props;
+ const { addEvent, getEventById, reorderEvent, deleteEvent } = useEventAction();
+
+ const handleCloneEvent = () => {
+ const currentEvent = getEventById(eventId);
+ if (!currentEvent || !isOntimeEvent(currentEvent)) {
+ return;
+ }
+
+ const newEvent = cloneEvent(currentEvent);
+ try {
+ addEvent(newEvent, { after: eventId });
+ } catch (_error) {
+ // we do not handle errors here
+ }
+ };
+
+ return (
+
+ } onClick={() => showModal(eventId)}>
+ Edit ...
+
+
+ } onClick={() => addEvent({ type: SupportedEvent.Event }, { before: eventId })}>
+ Add event above
+
+ } onClick={() => addEvent({ type: SupportedEvent.Event }, { after: eventId })}>
+ Add event below
+
+ } onClick={handleCloneEvent}>
+ Clone event
+
+
+ }
+ onClick={() => reorderEvent(eventId, entryIndex, entryIndex - 1)}
+ >
+ Move up
+
+ } onClick={() => reorderEvent(eventId, entryIndex, entryIndex + 1)}>
+ Move down
+
+ } onClick={() => deleteEvent([eventId])}>
+ Delete
+
+
+ );
+}
diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/useCuesheetTableMenu.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/useCuesheetTableMenu.tsx
new file mode 100644
index 000000000..b531399b6
--- /dev/null
+++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-menu/useCuesheetTableMenu.tsx
@@ -0,0 +1,31 @@
+import { create } from 'zustand';
+
+type Anchor = { x: number; y: number };
+
+type OpenMenu = {
+ isOpen: true;
+ eventId: string;
+ entryIndex: number;
+};
+
+type ClosedMenu = {
+ isOpen: false;
+ eventId: null;
+ entryIndex: null;
+};
+
+type CuesheetTableMenuStore = (OpenMenu | ClosedMenu) & {
+ position: Anchor;
+ openMenu: (position: Anchor, eventId: string, entryIndex: number) => void;
+ closeMenu: () => void;
+};
+
+export const useCuesheetTableMenu = create((set) => ({
+ isOpen: false,
+ eventId: null,
+ entryIndex: null,
+ position: { x: 0, y: 0 },
+ openMenu: (position: Anchor, eventId: string, entryIndex: number) =>
+ set({ isOpen: true, position, eventId, entryIndex }),
+ closeMenu: () => set({ isOpen: false }),
+}));