react-beautiful-dnd#ResponderProvided TypeScript Examples

The following examples show how to use react-beautiful-dnd#ResponderProvided. 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: TasksBoard.tsx    From projectboard with MIT License 5 votes vote down vote up
TasksBoard = () => {
    const backlogTasks: Array<Task> = useSelector((state: RootState) => state.taskList?.tasks.backlog);
    const todoTasks: Array<Task> = useSelector((state: RootState) => state.taskList?.tasks.todo);
    const inProgressTasks: Array<Task> = useSelector(
        (state: RootState) => state.taskList?.tasks?.in_progress
    );
    const doneTasks: Array<Task> = useSelector((state: RootState) => state.taskList?.tasks?.done);
    const canceledTasks: Array<Task> = useSelector((state: RootState) => state.taskList?.tasks?.cancelled);

    const todoSorted: Array<Task> = todoTasks.sort((prev: Task, next: Task) => prev.order - next.order);
    const backlogSorted: Array<Task> = backlogTasks.sort((prev: Task, next: Task) => prev.order - next.order);
    const inProgressSorted: Array<Task> = inProgressTasks.sort(
        (prev: Task, next: Task) => prev.order - next.order
    );
    const doneSorted: Array<Task> = doneTasks.sort((prev: Task, next: Task) => prev.order - next.order);
    const cancelledSorted: Array<Task> = canceledTasks.sort(
        (prev: Task, next: Task) => prev.order - next.order
    );

    const match = useRouteMatch<MatchParams>();
    const { getAccessTokenSilently } = useAuth0();

    // dispatch
    const dispatch = useDispatch<AppDispatch>();
    const onDragEnd = async (
        { source, destination, draggableId }: DropResult,
        provided: ResponderProvided
    ) => {
        if (source.droppableId === destination?.droppableId) return;
        if (!source || !destination) return;
        const token = await getAccessTokenSilently();
        dispatch(
            changeStatusOfTaskBoard(
                draggableId,
                source.droppableId,
                destination.droppableId,
                source.index,
                destination.index,
                match.params.projectId,
                token
            )
        );
    };

    return (
        <DragDropContext onDragEnd={onDragEnd}>
            <div className="flex flex-1 pt-6 pl-8 overflow-scroll bg-gray-100">
                <IssueCol title={'Backlog'} status={Status.BACKLOG} tasks={backlogSorted} />
                <IssueCol title={'Todo'} status={Status.TODO} tasks={todoSorted} />
                <IssueCol title={'In Progress'} status={Status.IN_PROGRESS} tasks={inProgressSorted} />
                <IssueCol title={'Done'} status={Status.DONE} tasks={doneSorted} />
                <IssueCol title={'Canceled'} status={Status.CANCELED} tasks={cancelledSorted} />
            </div>
        </DragDropContext>
    );
}
Example #2
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 #3
Source File: index.tsx    From nextjs-hasura-fullstack with MIT License 4 votes vote down vote up
BoardPage: React.FC = () => {
  const router = useRouter()
  const { setLastPosition } = useLastPositionNumber()

  const { data, loading } = useBoardQuery({
    variables: {
      id: Number(router.query.boardId),
    },
    skip: !router.query.boardId,
  })

  const [updateList] = useUpdateListMutation()
  const [updateCard] = useUpdateCardMutation()
  const [moveCard] = useMoveCardMutation()

  const renderLists = React.useMemo(() => {
    return data?.boards_by_pk?.lists || []
  }, [data])

  React.useEffect(() => {
    setLastPosition(renderLists[renderLists.length - 1]?.position || 1)
  }, [renderLists])

  const onDragEnd = async (result: DropResult, provided: ResponderProvided) => {
    const { source, destination, type } = result

    // dropped outside the list
    if (!destination) {
      return
    }

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

    // DragDrop a "List"
    if (type === 'LIST') {
      const updatedPosition = getUpdatePositionReorder(
        renderLists,
        source.index,
        destination.index,
      )
      const sourceList = renderLists[source.index]

      updateList({
        variables: {
          id: sourceList.id,
          name: sourceList.name,
          position: updatedPosition,
        },
        update: (cache, { data }) => {
          if (data?.update_lists_by_pk) {
            const cacheId = cache.identify({
              __typename: 'boards',
              id: sourceList.board_id,
            })
            if (cacheId) {
              cache.modify({
                id: cacheId,
                fields: {
                  lists(existingListRefs = []) {
                    return reorder(
                      existingListRefs,
                      source.index,
                      destination.index,
                    )
                  },
                },
              })
            }
          }
        },
        optimisticResponse: (variables) => {
          return {
            __typename: 'mutation_root',
            update_lists_by_pk: {
              ...sourceList,
              position: updatedPosition,
            },
          }
        },
      })
    }

    if (type === 'CARD') {
      /* same list: reorder card */
      if (source.droppableId === destination.droppableId) {
        const listCards = renderLists.find(
          (item) => item.id === Number(source.droppableId),
        )?.cards

        if (!listCards) {
          return
        }

        const updatedPosition = getUpdatePositionReorder(
          listCards,
          source.index,
          destination.index,
        )

        const sourceCard = listCards[source.index]

        await updateCard({
          variables: {
            id: sourceCard.id,
            title: sourceCard.title,
            position: updatedPosition,
          },
          update: (cache, { data }) => {
            if (data?.update_cards_by_pk) {
              const cacheId = cache.identify({
                __typename: 'lists',
                id: sourceCard.list_id,
              })
              if (cacheId) {
                cache.modify({
                  id: cacheId,
                  fields: {
                    cards(existingCardRefs = []) {
                      return reorder(
                        existingCardRefs,
                        source.index,
                        destination.index,
                      )
                    },
                  },
                })
              }
            }
          },
          optimisticResponse: (variables) => {
            return {
              __typename: 'mutation_root',
              update_cards_by_pk: {
                ...sourceCard,
                position: updatedPosition,
              },
            }
          },
        })
      } else {
        /**
         * Diferent list: move card
         */

        const sourceListCards = renderLists.find(
          (item) => item.id === Number(source.droppableId),
        )?.cards

        const destinationListCards = renderLists.find(
          (item) => item.id === Number(destination.droppableId),
        )?.cards

        if (!sourceListCards || !destinationListCards) {
          return
        }

        const updatedPosition = getUpdatePositionMove(
          sourceListCards,
          destinationListCards,
          source.index,
          destination.index,
        )

        const sourceCard = sourceListCards[source.index]

        await moveCard({
          variables: {
            id: sourceCard.id,
            list_id: Number(destination.droppableId),
            title: sourceCard.title,
            position: updatedPosition,
          },
          update: (cache, { data }) => {
            if (data?.update_cards_by_pk) {
              const cardCacheId = cache.identify(data.update_cards_by_pk)

              if (!cardCacheId) {
                return
              }

              const cacheId = cache.identify({
                __typename: 'lists',
                id: source.droppableId,
              })

              if (cacheId) {
                cache.modify({
                  id: cacheId,
                  fields: {
                    cards(existingRefs = []) {
                      const next = existingRefs.filter(
                        (listRef: { __ref: string }) =>
                          listRef.__ref !== cardCacheId,
                      )
                      return next
                    },
                  },
                })
              }

              const cacheIdDestination = cache.identify({
                __typename: 'lists',
                id: destination.droppableId,
              })

              if (cacheIdDestination) {
                cache.modify({
                  id: cacheIdDestination,
                  fields: {
                    cards(existingCardRefs = [], { toReference }) {
                      const moveRef = toReference(cardCacheId)
                      if (moveRef) {
                        const next = produce(
                          existingCardRefs as any[],
                          (draftState) => {
                            draftState.splice(destination.index, 0, moveRef)
                          },
                        )
                        return next
                      }
                    },
                  },
                })
              }
            }
          },
          optimisticResponse: () => ({
            __typename: 'mutation_root',
            update_cards_by_pk: {
              ...sourceCard,
              position: updatedPosition,
            },
          }),
        })
      }
    }
  }

  let listRender
  if (loading) {
    listRender = (
      <div className={`flex flex-no-wrap min-w-max-content`}>
        {[...Array(3)].map((item, idx) => (
          <div
            key={idx}
            className="flex flex-col max-w-sm p-4 mx-4 space-y-3 border border-gray-300 rounded-md shadow w-72"
          >
            <div className="flex space-x-4 animate-pulse">
              <div className="flex-1 py-1 space-y-4">
                <div className="w-3/4 h-4 bg-gray-400 rounded"></div>
                {/* <div className="space-y-2">
                  <div className="h-4 bg-gray-400 rounded"></div>
                  <div className="w-5/6 h-4 bg-gray-400 rounded"></div>
                </div> */}
              </div>
            </div>
            {[...Array(4)].map((item, index) => (
              <div
                key={index}
                className="flex flex-col max-w-sm p-4 border border-gray-300 rounded-md shadow"
              >
                <div className="flex space-x-4 animate-pulse">
                  <div className="flex-1 py-1 space-y-2">
                    <div className="h-4 bg-gray-400 rounded"></div>
                    <div className="w-5/6 h-4 bg-gray-400 rounded"></div>
                  </div>
                </div>
              </div>
            ))}
            <div className="flex flex-col max-w-sm border-t border-gray-300 rounded-md shadow">
              <div className="flex space-x-4 animate-pulse">
                <div className="flex-1 py-1 space-y-2">
                  <div className="h-4 bg-gray-400 rounded"></div>
                </div>
              </div>
            </div>
          </div>
        ))}
        {/*  */}
      </div>
    )
  } else {
    listRender = (
      <div className={`flex flex-no-wrap h-screen w-max-content`}>
        <DragDropContext
          onDragStart={(source) => {
            console.log(`?? [LOG]: source`, source)
          }}
          // onDragUpdate={onDragUpdate}
          onDragEnd={onDragEnd}
        >
          {/* List */}
          <Droppable
            droppableId="board"
            type="LIST"
            direction="horizontal"
            // ignoreContainerClipping={true}
          >
            {(
              { innerRef, droppableProps, placeholder },
              { isDraggingOver },
            ) => {
              return (
                <div
                  ref={innerRef}
                  {...droppableProps}
                  className={`inline-flex ${isDraggingOver && ``}`}
                  style={{
                    overflowAnchor: 'none',
                  }}
                >
                  {renderLists.map((list, index) => (
                    <ListBoard list={list} index={index} key={index} />
                  ))}
                  {placeholder}
                </div>
              )
            }}
          </Droppable>
        </DragDropContext>
        <div className={``}>
          <NewList lastId={renderLists[renderLists.length - 1]?.id || 0} />
        </div>
      </div>
    )
  }

  return (
    <div className={`h-screen space-x-2 -mt-14`}>
      <Scrollbar noScrollY className={``}>
        <div className={`pt-14`}>
          <div className={`mt-4 mb-8`}>
            <h2
              className={`text-3xl font-semibold text-center text-cool-gray-100`}
            >
              {data?.boards_by_pk?.icon} {data?.boards_by_pk?.name}
            </h2>
          </div>
          {listRender}
        </div>
      </Scrollbar>
    </div>
  )
}