react-beautiful-dnd#DroppableProvided TypeScript Examples

The following examples show how to use react-beautiful-dnd#DroppableProvided. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: TaskList.tsx    From knboard with MIT License 6 votes vote down vote up
TaskList = ({ columnId, listType, tasks: tasks, index }: Props) => (
  <Droppable droppableId={columnId.toString()} type={listType}>
    {(
      dropProvided: DroppableProvided,
      dropSnapshot: DroppableStateSnapshot
    ) => (
      <Wrapper
        isDraggingOver={dropSnapshot.isDraggingOver}
        isDraggingFrom={Boolean(dropSnapshot.draggingFromThisWith)}
        {...dropProvided.droppableProps}
      >
        <InnerList
          columnId={columnId}
          tasks={tasks}
          dropProvided={dropProvided}
          index={index}
        />
      </Wrapper>
    )}
  </Droppable>
)
Example #2
Source File: Tree.tsx    From react-beautiful-tree with Apache License 2.0 6 votes vote down vote up
patchDroppableProvided = (provided: DroppableProvided): DroppableProvided => {
    return {
      ...provided,
      innerRef: (el: HTMLElement) => {
        this.containerElement = el
        provided.innerRef(el)
      },
    }
  }
Example #3
Source File: dnd.ts    From revite with GNU Affero General Public License v3.0 5 votes vote down vote up
Droppable = rbdDroppable as unknown as (
    props: Omit<DroppableProps, "children"> & {
        children(
            provided: DroppableProvided,
            snapshot: DroppableStateSnapshot,
        ): JSX.Element;
    },
) => JSX.Element
Example #4
Source File: Tree.tsx    From react-beautiful-tree with Apache License 2.0 5 votes vote down vote up
render() {
    const {
      isNestingEnabled,
      isVirtualizationEnabled,
      virtualItemHeight,
    } = this.props
    const { flattenedTree } = this.state
    const renderedItems = this.renderItems()

    return (
      <DragDropContext
        onDragStart={this.onDragStart}
        onDragEnd={this.onDragEnd}
        onDragUpdate={this.onDragUpdate}
      >
        <Droppable
          droppableId="tree"
          isCombineEnabled={isNestingEnabled}
          ignoreContainerClipping
          mode={isVirtualizationEnabled ? 'virtual' : 'standard'}
          renderClone={
            isVirtualizationEnabled
              ? (provided, snapshot, rubric) =>
                  this.renderVirtualItem({
                    provided,
                    snapshot,
                    flatItem: flattenedTree[rubric.source.index],
                  })
              : undefined
          }
        >
          {(provided: DroppableProvided) => {
            const finalProvided: DroppableProvided = this.patchDroppableProvided(
              provided
            )

            return isVirtualizationEnabled ? (
              <AutoSizer defaultHeight={1} defaultWidth={1}>
                {({ height, width }: { height: number; width: number }) => (
                  <FixedSizeList
                    height={height}
                    itemCount={flattenedTree.length}
                    itemSize={virtualItemHeight}
                    width={width}
                    outerRef={provided.innerRef}
                    itemData={flattenedTree}
                  >
                    {this.renderVirtualRow}
                  </FixedSizeList>
                )}
              </AutoSizer>
            ) : (
              <div
                ref={finalProvided.innerRef}
                style={{ pointerEvents: 'auto' }}
                onTouchMove={this.onPointerMove}
                onMouseMove={this.onPointerMove}
                {...finalProvided.droppableProps}
              >
                {renderedItems}
                {provided.placeholder}
              </div>
            )
          }}
        </Droppable>
      </DragDropContext>
    )
  }
Example #5
Source File: BoardColumn.tsx    From projectboard with MIT License 5 votes vote down vote up
IssueCol = ({ title, status, tasks }: Props) => {
    let statusIcon = <StatusIcon status={status} />;

    let tasksItems = (tasks || []).map((task, idx) => <BoardItem task={task} index={idx} />);

    const history = useHistory();
    const match = useRouteMatch<MatchParams>();

    const onAddClick = () => {
        history.push(`/projects/${match.params.projectId}/create-task`, {
            status
        });
    };

    return (
        <div className="flex flex-col flex-shrink-0 mr-3 select-none w-90">
            <div className="flex items-center justify-between pb-3 text-sm">
                {/* left info */}
                <div className="flex items-center">
                    {statusIcon}
                    <span className="ml-3 mr-3 font-medium">{title}</span>
                    <span className="mr-3 font-normal text-gray-400">{tasks?.length || 0}</span>
                </div>

                {/* action buttons */}
                <div className="flex items-center">
                    <button
                        onClick={onAddClick}
                        className="flex items-center justify-center border-none rounded h-7 w-7 hover:bg-gray-200 focus:outline-none"
                    >
                        <AddIcon className="w-3.5 text-gray-400 hover:text-gray-700" />
                    </button>
                </div>
            </div>
            <Droppable droppableId={status} key={status} type="category">
                {(provided: DroppableProvided) => {
                    return (
                        <div
                            ref={provided.innerRef}
                            {...provided.droppableProps}
                            className="flex flex-col flex-1 w-full overflow-y-auto border-gray-200 pt-0.5"
                        >
                            {React.Children.toArray(tasksItems)}
                            {provided.placeholder}
                        </div>
                    );
                }}
            </Droppable>
        </div>
    );
}
Example #6
Source File: main.tsx    From webminidisc with GNU General Public License v2.0 4 votes vote down vote up
Main = (props: {}) => {
    let dispatch = useDispatch();
    const disc = useShallowEqualSelector(state => state.main.disc);
    const deviceName = useShallowEqualSelector(state => state.main.deviceName);
    const deviceStatus = useShallowEqualSelector(state => state.main.deviceStatus);
    const { vintageMode } = useShallowEqualSelector(state => state.appState);

    const [selected, setSelected] = React.useState<number[]>([]);
    const [uploadedFiles, setUploadedFiles] = React.useState<File[]>([]);
    const [lastClicked, setLastClicked] = useState(-1);
    const [moveMenuAnchorEl, setMoveMenuAnchorEl] = React.useState<null | HTMLElement>(null);

    const handleShowMoveMenu = useCallback(
        (event: React.MouseEvent<HTMLButtonElement>) => {
            setMoveMenuAnchorEl(event.currentTarget);
        },
        [setMoveMenuAnchorEl]
    );
    const handleCloseMoveMenu = useCallback(() => {
        setMoveMenuAnchorEl(null);
    }, [setMoveMenuAnchorEl]);

    const handleMoveSelectedTrack = useCallback(
        (destIndex: number) => {
            dispatch(moveTrack(selected[0], destIndex));
            handleCloseMoveMenu();
        },
        [dispatch, selected, handleCloseMoveMenu]
    );

    const handleDrop = useCallback(
        (result: DropResult, provided: ResponderProvided) => {
            if (!result.destination) return;
            let sourceList = parseInt(result.source.droppableId),
                sourceIndex = result.source.index,
                targetList = parseInt(result.destination.droppableId),
                targetIndex = result.destination.index;
            dispatch(dragDropTrack(sourceList, sourceIndex, targetList, targetIndex));
        },
        [dispatch]
    );

    const handleShowDumpDialog = useCallback(() => {
        dispatch(dumpDialogActions.setVisible(true));
    }, [dispatch]);

    useEffect(() => {
        dispatch(listContent());
    }, [dispatch]);

    useEffect(() => {
        setSelected([]); // Reset selection if disc changes
    }, [disc]);

    const onDrop = useCallback(
        (acceptedFiles: File[], rejectedFiles: File[]) => {
            setUploadedFiles(acceptedFiles);
            dispatch(convertDialogActions.setVisible(true));
        },
        [dispatch]
    );

    const { getRootProps, getInputProps, isDragActive, open } = useDropzone({
        onDrop,
        accept: [`audio/*`, `video/mp4`],
        noClick: true,
    });

    const classes = useStyles();
    const tracks = useMemo(() => getSortedTracks(disc), [disc]);
    const groupedTracks = useMemo(() => getGroupedTracks(disc), [disc]);

    // Action Handlers
    const handleSelectTrackClick = useCallback(
        (event: React.MouseEvent, item: number) => {
            if (event.shiftKey && selected.length && lastClicked !== -1) {
                let rangeBegin = Math.min(lastClicked + 1, item),
                    rangeEnd = Math.max(lastClicked - 1, item);
                let copy = [...selected];
                for (let i = rangeBegin; i <= rangeEnd; i++) {
                    let index = copy.indexOf(i);
                    if (index === -1) copy.push(i);
                    else copy.splice(index, 1);
                }
                if (!copy.includes(item)) copy.push(item);
                setSelected(copy);
            } else if (selected.includes(item)) {
                setSelected(selected.filter(i => i !== item));
            } else {
                setSelected([...selected, item]);
            }
            setLastClicked(item);
        },
        [selected, setSelected, lastClicked, setLastClicked]
    );

    const handleSelectAllClick = useCallback(
        (event: React.ChangeEvent<HTMLInputElement>) => {
            if (selected.length < tracks.length) {
                setSelected(tracks.map(t => t.index));
            } else {
                setSelected([]);
            }
        },
        [selected, tracks]
    );

    const handleRenameTrack = useCallback(
        (event: React.MouseEvent, index: number) => {
            let track = tracks.find(t => t.index === index);
            if (!track) {
                return;
            }

            dispatch(
                batchActions([
                    renameDialogActions.setVisible(true),
                    renameDialogActions.setGroupIndex(null),
                    renameDialogActions.setCurrentName(track.title),
                    renameDialogActions.setCurrentFullWidthName(track.fullWidthTitle),
                    renameDialogActions.setIndex(track.index),
                ])
            );
        },
        [dispatch, tracks]
    );

    const handleRenameGroup = useCallback(
        (event: React.MouseEvent, index: number) => {
            let group = groupedTracks.find(g => g.index === index);
            if (!group) {
                return;
            }

            dispatch(
                batchActions([
                    renameDialogActions.setVisible(true),
                    renameDialogActions.setGroupIndex(index),
                    renameDialogActions.setCurrentName(group.title ?? ''),
                    renameDialogActions.setCurrentFullWidthName(group.fullWidthTitle ?? ''),
                    renameDialogActions.setIndex(-1),
                ])
            );
        },
        [dispatch, groupedTracks]
    );

    const handleRenameActionClick = useCallback(
        (event: React.MouseEvent) => {
            if (event.detail !== 1) return; //Event retriggering when hitting enter in the dialog
            handleRenameTrack(event, selected[0]);
        },
        [handleRenameTrack, selected]
    );

    const handleDeleteSelected = useCallback(
        (event: React.MouseEvent) => {
            dispatch(deleteTracks(selected));
        },
        [dispatch, selected]
    );

    const handleGroupTracks = useCallback(
        (event: React.MouseEvent) => {
            dispatch(groupTracks(selected));
        },
        [dispatch, selected]
    );

    const handleDeleteGroup = useCallback(
        (event: React.MouseEvent, index: number) => {
            dispatch(deleteGroup(index));
        },
        [dispatch]
    );

    const handleTogglePlayPauseTrack = useCallback(
        (event: React.MouseEvent, track: number) => {
            if (!deviceStatus) {
                return;
            }
            if (deviceStatus.track !== track) {
                dispatch(control('goto', track));
                dispatch(control('play'));
            } else if (deviceStatus.state === 'playing') {
                dispatch(control('pause'));
            } else {
                dispatch(control('play'));
            }
        },
        [dispatch, deviceStatus]
    );

    const canGroup = useMemo(() => {
        return (
            tracks.filter(n => n.group === null && selected.includes(n.index)).length === selected.length &&
            isSequential(selected.sort((a, b) => a - b))
        );
    }, [tracks, selected]);
    const selectedCount = selected.length;

    if (vintageMode) {
        const p = {
            disc,
            deviceName,

            selected,
            setSelected,
            selectedCount,

            tracks,
            uploadedFiles,
            setUploadedFiles,

            onDrop,
            getRootProps,
            getInputProps,
            isDragActive,
            open,

            moveMenuAnchorEl,
            setMoveMenuAnchorEl,

            handleShowMoveMenu,
            handleCloseMoveMenu,
            handleMoveSelectedTrack,
            handleShowDumpDialog,
            handleDeleteSelected,
            handleRenameActionClick,
            handleRenameTrack,
            handleSelectAllClick,
            handleSelectTrackClick,
        };
        return <W95Main {...p} />;
    }

    return (
        <React.Fragment>
            <Box className={classes.headBox}>
                <Typography component="h1" variant="h4">
                    {deviceName || `Loading...`}
                </Typography>
                <TopMenu />
            </Box>
            <Typography component="h2" variant="body2">
                {disc !== null ? (
                    <React.Fragment>
                        <span>{`${formatTimeFromFrames(disc.left, false)} left of ${formatTimeFromFrames(disc.total, false)} `}</span>
                        <Tooltip
                            title={
                                <React.Fragment>
                                    <span>{`${formatTimeFromFrames(disc.left * 2, false)} left in LP2 Mode`}</span>
                                    <br />
                                    <span>{`${formatTimeFromFrames(disc.left * 4, false)} left in LP4 Mode`}</span>
                                </React.Fragment>
                            }
                            arrow
                        >
                            <span className={classes.remainingTimeTooltip}>SP Mode</span>
                        </Tooltip>
                    </React.Fragment>
                ) : (
                    `Loading...`
                )}
            </Typography>
            <Toolbar
                className={clsx(classes.toolbar, {
                    [classes.toolbarHighlight]: selectedCount > 0,
                })}
            >
                {selectedCount > 0 ? (
                    <Checkbox
                        indeterminate={selectedCount > 0 && selectedCount < tracks.length}
                        checked={selectedCount > 0}
                        onChange={handleSelectAllClick}
                        inputProps={{ 'aria-label': 'select all tracks' }}
                    />
                ) : null}
                {selectedCount > 0 ? (
                    <Typography className={classes.toolbarLabel} color="inherit" variant="subtitle1">
                        {selectedCount} selected
                    </Typography>
                ) : (
                    <Typography component="h3" variant="h6" className={classes.toolbarLabel}>
                        {disc?.fullWidthTitle && `${disc.fullWidthTitle} / `}
                        {disc?.title || `Untitled Disc`}
                    </Typography>
                )}
                {selectedCount > 0 ? (
                    <React.Fragment>
                        <Tooltip title="Record from MD">
                            <Button aria-label="Record" onClick={handleShowDumpDialog}>
                                Record
                            </Button>
                        </Tooltip>
                    </React.Fragment>
                ) : null}

                {selectedCount > 0 ? (
                    <Tooltip title="Delete">
                        <IconButton aria-label="delete" onClick={handleDeleteSelected}>
                            <DeleteIcon />
                        </IconButton>
                    </Tooltip>
                ) : null}

                {selectedCount > 0 ? (
                    <Tooltip title={canGroup ? 'Group' : ''}>
                        <IconButton aria-label="group" disabled={!canGroup} onClick={handleGroupTracks}>
                            <CreateNewFolderIcon />
                        </IconButton>
                    </Tooltip>
                ) : null}

                {selectedCount > 0 ? (
                    <Tooltip title="Rename">
                        <IconButton aria-label="rename" disabled={selectedCount !== 1} onClick={handleRenameActionClick}>
                            <EditIcon />
                        </IconButton>
                    </Tooltip>
                ) : null}
            </Toolbar>
            <Box className={classes.main} {...getRootProps()} id="main">
                <input {...getInputProps()} />
                <Table size="small">
                    <TableHead>
                        <TableRow>
                            <TableCell className={classes.dragHandleEmpty}></TableCell>
                            <TableCell className={classes.indexCell}>#</TableCell>
                            <TableCell>Title</TableCell>
                            <TableCell align="right">Duration</TableCell>
                        </TableRow>
                    </TableHead>
                    <DragDropContext onDragEnd={handleDrop}>
                        <TableBody>
                            {groupedTracks.map((group, index) => (
                                <TableRow key={`${index}`}>
                                    <TableCell colSpan={4} style={{ padding: '0' }}>
                                        <Table size="small">
                                            <Droppable droppableId={`${index}`} key={`${index}`}>
                                                {(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => (
                                                    <TableBody
                                                        {...provided.droppableProps}
                                                        ref={provided.innerRef}
                                                        className={clsx({ [classes.hoveringOverGroup]: snapshot.isDraggingOver })}
                                                    >
                                                        {group.title !== null && (
                                                            <GroupRow
                                                                group={group}
                                                                onRename={handleRenameGroup}
                                                                onDelete={handleDeleteGroup}
                                                            />
                                                        )}
                                                        {group.title === null && group.tracks.length === 0 && (
                                                            <TableRow style={{ height: '1px' }} />
                                                        )}
                                                        {group.tracks.map((t, tidx) => (
                                                            <Draggable
                                                                draggableId={`${group.index}-${t.index}`}
                                                                key={`t-${t.index}`}
                                                                index={tidx}
                                                            >
                                                                {(provided: DraggableProvided) => (
                                                                    <TrackRow
                                                                        track={t}
                                                                        draggableProvided={provided}
                                                                        inGroup={group.title !== null}
                                                                        isSelected={selected.includes(t.index)}
                                                                        trackStatus={getTrackStatus(t, deviceStatus)}
                                                                        onSelect={handleSelectTrackClick}
                                                                        onRename={handleRenameTrack}
                                                                        onTogglePlayPause={handleTogglePlayPauseTrack}
                                                                    />
                                                                )}
                                                            </Draggable>
                                                        ))}
                                                        {provided.placeholder}
                                                    </TableBody>
                                                )}
                                            </Droppable>
                                        </Table>
                                    </TableCell>
                                </TableRow>
                            ))}
                        </TableBody>
                    </DragDropContext>
                </Table>
                {isDragActive ? (
                    <Backdrop className={classes.backdrop} open={isDragActive}>
                        Drop your Music to Upload
                    </Backdrop>
                ) : null}
            </Box>
            <Fab color="primary" aria-label="add" className={classes.add} onClick={open}>
                <AddIcon />
            </Fab>

            <UploadDialog />
            <RenameDialog />
            <ErrorDialog />
            <ConvertDialog files={uploadedFiles} />
            <RecordDialog />
            <DumpDialog trackIndexes={selected} />
            <AboutDialog />
            <PanicDialog />
        </React.Fragment>
    );
}
Example #7
Source File: PipelineEditComponents.tsx    From baleen3 with Apache License 2.0 4 votes vote down vote up
PipelineEditComponents: React.FC<PipelineEditComponentsProps> = ({
  id,
  type,
  components,
  addComponents,
  moveComponent,
  removeComponent,
  setComponentName,
  setComponentSettings,
}: PipelineEditComponentsProps) => {
  const handleDrag = (result: DropResult): void => {
    const { source, destination } = result

    if (destination === undefined) {
      return
    }

    if (destination.index === source.index) {
      return
    }

    moveComponent(source.index, destination.index)
  }
  return (
    <DragDropContext onDragEnd={handleDrag}>
      <Droppable droppableId={id}>
        {(provided: DroppableProvided): React.ReactElement => (
          <Column
            alignItems="center"
            {...provided.droppableProps}
            // eslint-disable-next-line @typescript-eslint/unbound-method
            ref={provided.innerRef}
          >
            {components.map((component, index) => {
              return (
                <Draggable
                  key={component.id}
                  draggableId={component.id}
                  index={index}
                >
                  {(
                    provider: DraggableProvided,
                    snapshot: DraggableStateSnapshot
                  ): React.ReactElement => (
                    <Column
                      width={1}
                      alignItems="center"
                      // eslint-disable-next-line @typescript-eslint/unbound-method
                      ref={provider.innerRef}
                      {...provider.draggableProps}
                    >
                      <PipelineEditComponentSeparator
                        onInsert={(components: ComponentInfo[]): void =>
                          addComponents(components, index)
                        }
                        isDragging={snapshot.isDragging}
                        type={type}
                      />
                      <PipelineComponent
                        {...provider.dragHandleProps}
                        type={type}
                        descriptor={component}
                        setName={setComponentName}
                        setSettings={setComponentSettings}
                        onDelete={removeComponent}
                      />
                    </Column>
                  )}
                </Draggable>
              )
            })}
            {provided.placeholder}
            <PipelineEditComponentSeparator
              onInsert={(components: ComponentInfo[]): void =>
                addComponents(components)
              }
              isDragging={false}
              type={type}
            />
          </Column>
        )}
      </Droppable>
    </DragDropContext>
  )
}
Example #8
Source File: Board.tsx    From knboard with MIT License 4 votes vote down vote up
Board = () => {
  const detail = useSelector((state: RootState) => state.board.detail);
  const error = useSelector((state: RootState) => state.board.detailError);
  const columns = useSelector(columnSelectors.selectAll);
  const tasksByColumn = useSelector((state: RootState) => state.task.byColumn);
  const tasksById = useSelector((state: RootState) => state.task.byId);
  const dispatch = useDispatch();
  const { id } = useParams();

  React.useEffect(() => {
    if (id) {
      dispatch(fetchBoardById({ boardId: id }));
    }
  }, [id]);

  const onDragEnd = (result: DropResult) => {
    // dropped nowhere
    if (!result.destination) {
      return;
    }

    const source: DraggableLocation = result.source;
    const destination: DraggableLocation = result.destination;

    // did not move anywhere - can bail early
    if (
      source.droppableId === destination.droppableId &&
      source.index === destination.index
    ) {
      return;
    }

    // reordering column
    if (result.type === "COLUMN") {
      const newOrdered: IColumn[] = reorder(
        columns,
        source.index,
        destination.index
      );
      dispatch(updateColumns(newOrdered));
      return;
    }

    const data = reorderTasks({
      tasksByColumn,
      source,
      destination,
    });
    dispatch(updateTasksByColumn(data.tasksByColumn));
  };

  const detailDataExists = detail?.id.toString() === id;

  if (error) {
    return <PageError>{error}</PageError>;
  }

  if (!detailDataExists) {
    return <Spinner loading={!detailDataExists} />;
  }

  return (
    <>
      <SEO title={detail?.name} />
      {columns.length !== 0 ? (
        <BoardContainer data-testid="board-container">
          <ColumnsBlock>
            <DragDropContext onDragEnd={onDragEnd}>
              <Droppable
                droppableId="board"
                type="COLUMN"
                direction="horizontal"
              >
                {(provided: DroppableProvided) => (
                  <ColumnContainer
                    ref={provided.innerRef}
                    {...provided.droppableProps}
                  >
                    {columns.map((column: IColumn, index: number) => (
                      <Column
                        key={column.id}
                        id={column.id}
                        title={column.title}
                        index={index}
                        tasks={tasksByColumn[column.id].map(
                          (taskId) => tasksById[taskId]
                        )}
                      />
                    ))}
                    {provided.placeholder}
                  </ColumnContainer>
                )}
              </Droppable>
            </DragDropContext>
          </ColumnsBlock>
          <RightMargin />
        </BoardContainer>
      ) : (
        <EmptyBoard>This board is empty.</EmptyBoard>
      )}
    </>
  );
}
Example #9
Source File: MediaMenu.tsx    From sync-party with GNU General Public License v3.0 4 votes vote down vote up
export default function MediaMenu({
    socket,
    setPlayerFocused,
    emitPlayWish,
    freezeUiVisible,
    isPlaying,
    playerState
}: Props): JSX.Element {
    const party: ClientParty | null = useSelector(
        (state: RootAppState) => state.globalState.party
    );
    const playingItem: MediaItem | null = useSelector(
        (state: RootAppState) => state.globalState.playingItem
    );
    const uiVisible = useSelector(
        (state: RootAppState) => state.globalState.uiVisible
    );
    const uiFocused = useSelector(
        (state: RootAppState) => state.globalState.uiFocused
    );

    const [hoverTimestamp, setHoverTimestamp] = useState(0);
    const hoverTimestampRef = useRef(hoverTimestamp);
    hoverTimestampRef.current = hoverTimestamp;

    const [addMediaIsActive, setAddMediaIsActive] = useState(false);

    const [partyItemsSet, setPartyItemsSet] = useState<Set<string>>(new Set());

    const partyItemListRef = useRef<HTMLDivElement | null>(null);

    const dispatch = useDispatch();
    const { t } = useTranslation();

    // Collect items in active party in a set for faster checks
    useEffect(() => {
        if (party) {
            const itemsSet = new Set<string>();

            party.items.forEach((item) => {
                itemsSet.add(item.id);
            });

            setPartyItemsSet(itemsSet);
        }
    }, [party]);

    const handleRemoveItemFromParty = async (
        item: MediaItem
    ): Promise<void> => {
        if (socket && party) {
            if (
                party.items.length === 1 &&
                playerState.playingItem &&
                playerState.playingItem.id === item.id
            ) {
                dispatch(
                    setGlobalState({
                        errorMessage: t(`errors.removeLastItemError`)
                    })
                );

                return Promise.reject();
            }

            try {
                const response = await Axios.delete('/api/partyItems', {
                    data: { itemId: item.id, partyId: party.id },
                    ...axiosConfig()
                });

                if (response.data.success) {
                    const currentIndex = playerState.playlistIndex;

                    if (
                        playerState.playingItem &&
                        playerState.playingItem.id === item.id
                    ) {
                        emitPlayWish(
                            party.items[
                                currentIndex < party.items.length - 1
                                    ? currentIndex + 1
                                    : currentIndex - 1
                            ],
                            playerState.isPlaying,
                            null,
                            false,
                            0
                        );
                    }

                    socket.emit('partyUpdate', {
                        partyId: party.id
                    });

                    setPlayerFocused(true);
                } else {
                    dispatch(
                        setGlobalState({
                            errorMessage: t(
                                `apiResponseMessages.${response.data.msg}`
                            )
                        })
                    );
                }
            } catch (error) {
                dispatch(
                    setGlobalState({
                        errorMessage: t(`errors.removeItemError`)
                    })
                );
            }
        }
    };

    const handleItemEditSave = async (item: MediaItem): Promise<void> => {
        if (socket && party) {
            try {
                const response = await Axios.put(
                    '/api/mediaItem/' + item.id,
                    item,
                    axiosConfig()
                );

                if (response.data.success) {
                    await getUpdatedUserItems(dispatch, t);

                    socket.emit('partyUpdate', { partyId: party.id });
                    setPlayerFocused(true);
                } else {
                    dispatch(
                        setGlobalState({
                            errorMessage: t(
                                `apiResponseMessages.${response.data.msg}`
                            )
                        })
                    );
                }
            } catch (error) {
                dispatch(
                    setGlobalState({
                        errorMessage: t(`errors.itemSaveError`)
                    })
                );
            }
        }
    };

    const handleItemClick = (chosenItem: MediaItem): void => {
        if (party) {
            const newSource = party.items.filter((mediaItem: MediaItem) => {
                return mediaItem.id === chosenItem.id;
            })[0];

            emitPlayWish(
                newSource,
                true,
                playerState.playingItem ? playerState.playingItem.id : null,
                true,
                0
            );
        }
    };

    const onDragEnd = async (result: DropResult): Promise<void> => {
        if (socket && party) {
            // Dropped outside the list
            if (!result.destination) {
                return;
            }

            const orderedItems = reorderItems(
                party.items,
                result.source.index,
                result.destination.index
            );

            try {
                const response = await Axios.put(
                    '/api/partyItems',
                    { orderedItems: orderedItems, partyId: party.id },
                    axiosConfig()
                );

                if (response.data.success) {
                    socket.emit('partyUpdate', { partyId: party.id });
                } else {
                    dispatch(
                        setGlobalState({
                            errorMessage: t(
                                `apiResponseMessages.${response.data.msg}`
                            )
                        })
                    );
                }
            } catch (error) {
                dispatch(
                    setGlobalState({
                        errorMessage: t(`errors.reorderError`)
                    })
                );
            }
        }
    };

    return (
        <div
            className={
                'mediaMenu fixed top-0 right-0 flex flex-col mt-16 p-2 border border-gray-500 rounded m-2 shadow-md backgroundShade z-50' +
                (uiVisible || !playingItem || !playingItem.url ? '' : ' hidden')
            }
            onMouseOver={(): void => {
                const now = Date.now();

                if (hoverTimestampRef.current + 10000 < now) {
                    freezeUiVisible(true);
                    setHoverTimestamp(now);
                }
            }}
            onMouseLeave={(): void => {
                if (!uiFocused.chat && !addMediaIsActive) {
                    freezeUiVisible(false);
                    setHoverTimestamp(0);
                }
            }}
        >
            {party && party.items.length ? (
                <div className="partyItemList" ref={partyItemListRef}>
                    <DragDropContext onDragEnd={onDragEnd}>
                        <Droppable droppableId="droppable">
                            {(provided: DroppableProvided): JSX.Element => (
                                <div
                                    {...provided.droppableProps}
                                    ref={provided.innerRef}
                                >
                                    {party.items.map(
                                        (
                                            mediaItem: MediaItem,
                                            index: number
                                        ) => {
                                            const isCurrentlyPlayingItem =
                                                playingItem &&
                                                playingItem.id === mediaItem.id
                                                    ? true
                                                    : false;
                                            const alreadyPlayed =
                                                party.metadata.played &&
                                                party.metadata.played[
                                                    mediaItem.id
                                                ]
                                                    ? true
                                                    : false;

                                            return (
                                                <MediaMenuDraggable
                                                    key={mediaItem.id}
                                                    mediaItemId={mediaItem.id}
                                                    index={index}
                                                >
                                                    <ItemListed
                                                        item={mediaItem}
                                                        isPlaying={isPlaying}
                                                        isCurrentlyPlayingItem={
                                                            isCurrentlyPlayingItem
                                                        }
                                                        alreadyPlayed={
                                                            alreadyPlayed
                                                        }
                                                        nameEditingAllowed={
                                                            false
                                                        }
                                                        handleItemClick={(): void =>
                                                            handleItemClick(
                                                                mediaItem
                                                            )
                                                        }
                                                        onRemoveButtonClick={(
                                                            item: MediaItem
                                                        ): void => {
                                                            handleRemoveItemFromParty(
                                                                item
                                                            );
                                                        }}
                                                        handleItemSave={(
                                                            item: MediaItem
                                                        ): void => {
                                                            handleItemEditSave(
                                                                item
                                                            );
                                                        }}
                                                        setPlayerFocused={
                                                            setPlayerFocused
                                                        }
                                                        partyItemListRef={
                                                            partyItemListRef
                                                        }
                                                    ></ItemListed>
                                                </MediaMenuDraggable>
                                            );
                                        }
                                    )}
                                </div>
                            )}
                        </Droppable>
                    </DragDropContext>
                </div>
            ) : (
                <div className="mb-2">There&#39;s nothing.</div>
            )}
            <AddMedia
                isActive={addMediaIsActive}
                partyItemsSet={partyItemsSet}
                setAddMediaIsActive={setAddMediaIsActive}
                handleItemEditSave={handleItemEditSave}
                setPlayerFocused={(focused: boolean): void =>
                    setPlayerFocused(focused)
                }
                socket={socket}
            ></AddMedia>
        </div>
    );
}