mirror of
https://github.com/cpvalente/ontime.git
synced 2026-07-26 10:38:55 +00:00
refactor: use a single menu for table
This commit is contained in:
committed by
Carlos Valente
parent
86bb739d90
commit
84a5b4fe5b
@@ -22,6 +22,7 @@ import BlockRow from './cuesheet-table-elements/BlockRow';
|
||||
import CuesheetHeader from './cuesheet-table-elements/CuesheetHeader';
|
||||
import DelayRow from './cuesheet-table-elements/DelayRow';
|
||||
import EventRow from './cuesheet-table-elements/EventRow';
|
||||
import CuesheetTableMenu from './cuesheet-table-menu/CuesheetTableMenu';
|
||||
import CuesheetTableSettings from './cuesheet-table-settings/CuesheetTableSettings';
|
||||
import useColumnManager from './useColumnManager';
|
||||
|
||||
@@ -172,7 +173,6 @@ export default function CuesheetTable(props: CuesheetTableProps) {
|
||||
selectedRef={isSelected ? selectedRef : undefined}
|
||||
skip={entry.skip}
|
||||
colour={entry.colour}
|
||||
showModal={showModal}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
return (
|
||||
@@ -191,6 +191,7 @@ export default function CuesheetTable(props: CuesheetTableProps) {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CuesheetTableMenu showModal={showModal} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
+16
-14
@@ -1,11 +1,11 @@
|
||||
import { memo, MutableRefObject, PropsWithChildren, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { IconButton, Menu, MenuButton } from '@chakra-ui/react';
|
||||
import { IconButton } from '@chakra-ui/react';
|
||||
import { IoEllipsisHorizontal } from '@react-icons/all-files/io5/IoEllipsisHorizontal';
|
||||
import Color from 'color';
|
||||
|
||||
import { cx, getAccessibleColour } from '../../../../common/utils/styleUtils';
|
||||
import { useCuesheetOptions } from '../../cuesheet.options';
|
||||
import CuesheetTableMenu from '../cuesheet-table-menu/CuesheetTableMenu';
|
||||
import { useCuesheetTableMenu } from '../cuesheet-table-menu/useCuesheetTableMenu';
|
||||
|
||||
import style from '../CuesheetTable.module.scss';
|
||||
|
||||
@@ -17,15 +17,16 @@ interface EventRowProps {
|
||||
selectedRef?: MutableRefObject<HTMLTableRowElement | null>;
|
||||
skip?: boolean;
|
||||
colour?: string;
|
||||
showModal: (eventId: string) => void;
|
||||
}
|
||||
|
||||
function EventRow(props: PropsWithChildren<EventRowProps>) {
|
||||
const { children, eventId, eventIndex, rowIndex, isPast, selectedRef, skip, colour, showModal } = props;
|
||||
const { children, eventId, eventIndex, rowIndex, isPast, selectedRef, skip, colour } = props;
|
||||
const { hideIndexColumn, showActionMenu } = useCuesheetOptions();
|
||||
const ownRef = useRef<HTMLTableRowElement>(null);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const { openMenu } = useCuesheetTableMenu();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
@@ -64,16 +65,17 @@ function EventRow(props: PropsWithChildren<EventRowProps>) {
|
||||
>
|
||||
{showActionMenu && (
|
||||
<td className={style.actionColumn}>
|
||||
<Menu variant='ontime-on-dark' size='sm' isLazy>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
size='sm'
|
||||
aria-label='Options'
|
||||
icon={<IoEllipsisHorizontal />}
|
||||
variant='ontime-subtle'
|
||||
/>
|
||||
<CuesheetTableMenu eventId={eventId} entryIndex={rowIndex} showModal={showModal} />
|
||||
</Menu>
|
||||
<IconButton
|
||||
size='sm'
|
||||
aria-label='Options'
|
||||
icon={<IoEllipsisHorizontal />}
|
||||
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);
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
{!hideIndexColumn && (
|
||||
|
||||
+23
-58
@@ -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 (
|
||||
<Portal>
|
||||
<MenuList>
|
||||
<MenuItem icon={<IoOptions />} onClick={() => showModal(eventId)}>
|
||||
Edit ...
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem icon={<IoAdd />} onClick={() => addEvent({ type: SupportedEvent.Event }, { before: eventId })}>
|
||||
Add event above
|
||||
</MenuItem>
|
||||
<MenuItem icon={<IoAdd />} onClick={() => addEvent({ type: SupportedEvent.Event }, { after: eventId })}>
|
||||
Add event below
|
||||
</MenuItem>
|
||||
<MenuItem icon={<IoDuplicateOutline />} onClick={handleCloneEvent}>
|
||||
Clone event
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
isDisabled={entryIndex < 1}
|
||||
icon={<IoArrowUp />}
|
||||
onClick={() => reorderEvent(eventId, entryIndex, entryIndex - 1)}
|
||||
>
|
||||
Move up
|
||||
</MenuItem>
|
||||
<MenuItem icon={<IoArrowDown />} onClick={() => reorderEvent(eventId, entryIndex, entryIndex + 1)}>
|
||||
Move down
|
||||
</MenuItem>
|
||||
<MenuItem icon={<IoTrash />} onClick={() => deleteEvent([eventId])}>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
{isOpen && (
|
||||
<Menu isOpen size='sm' onClose={closeMenu} isLazy variant='ontime-on-dark'>
|
||||
<MenuButton
|
||||
position='absolute'
|
||||
left={position.x}
|
||||
top={position.y}
|
||||
pointerEvents='none'
|
||||
aria-hidden
|
||||
w={1}
|
||||
h={1}
|
||||
/>
|
||||
<CuesheetTableMenuActionsProps eventId={eventId} entryIndex={entryIndex} showModal={showModal} />
|
||||
</Menu>
|
||||
)}
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
+68
@@ -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 (
|
||||
<MenuList>
|
||||
<MenuItem icon={<IoOptions />} onClick={() => showModal(eventId)}>
|
||||
Edit ...
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem icon={<IoAdd />} onClick={() => addEvent({ type: SupportedEvent.Event }, { before: eventId })}>
|
||||
Add event above
|
||||
</MenuItem>
|
||||
<MenuItem icon={<IoAdd />} onClick={() => addEvent({ type: SupportedEvent.Event }, { after: eventId })}>
|
||||
Add event below
|
||||
</MenuItem>
|
||||
<MenuItem icon={<IoDuplicateOutline />} onClick={handleCloneEvent}>
|
||||
Clone event
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
isDisabled={entryIndex < 1}
|
||||
icon={<IoArrowUp />}
|
||||
onClick={() => reorderEvent(eventId, entryIndex, entryIndex - 1)}
|
||||
>
|
||||
Move up
|
||||
</MenuItem>
|
||||
<MenuItem icon={<IoArrowDown />} onClick={() => reorderEvent(eventId, entryIndex, entryIndex + 1)}>
|
||||
Move down
|
||||
</MenuItem>
|
||||
<MenuItem icon={<IoTrash />} onClick={() => deleteEvent([eventId])}>
|
||||
Delete
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
);
|
||||
}
|
||||
+31
@@ -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<CuesheetTableMenuStore>((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 }),
|
||||
}));
|
||||
Reference in New Issue
Block a user