react-beautiful-dnd#Droppable TypeScript Examples

The following examples show how to use react-beautiful-dnd#Droppable. 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: FlowList.tsx    From Protoman with MIT License 6 votes vote down vote up
FlowList: React.FunctionComponent<Props> = ({ collectionName }) => {
  const dispatch = useDispatch();

  const collection = useSelector((s: AppState) => getByKey(s.collections, collectionName));
  const flowNames = useSelector((s: AppState) => collection?.flows?.map(([n]) => n));
  const isCurrentCollection = useSelector((s: AppState) => s.currentCollection === collectionName);
  const currentFlow = useSelector((s: AppState) => s.currentFlow);

  function handleSelection(flowName: string): void {
    dispatch(selectFlow(collectionName, flowName));
  }

  function validateFlowName(flowName: string): boolean {
    return !collection?.flows?.map(([n]) => n)?.includes(flowName);
  }

  function handleDelete(flowName: string): void {
    const flowCount = collection?.flows?.length || 0;
    if (flowCount > 1) {
      dispatch(deleteFlow(collectionName, flowName));
    } else {
      message.error("Can't delete the last request");
    }
  }

  function handleClone(originalFlowName: string): void {
    //check if this clone already exists
    const tmpName = originalFlowName.concat('_clone');
    let tmpNameIdx = 1;
    while (!validateFlowName(`${tmpName}${tmpNameIdx}`)) tmpNameIdx++;
    dispatch(cloneFlow(collectionName, originalFlowName, `${tmpName}${tmpNameIdx}`));
  }

  function handleDragEnd(result: DropResult): void {
    console.log(result);
    if (!result.destination || result.source.droppableId != result.destination.droppableId) return;

    const src = result.source.index;
    const dst = result.destination.index;

    dispatch(reorderFlow(collectionName, src, dst));
  }

  return (
    <DragDropContext onDragEnd={handleDragEnd}>
      <Droppable droppableId={collectionName}>
        {(provided): React.ReactElement => (
          <div {...provided.droppableProps} ref={provided.innerRef}>
            <List
              dataSource={flowNames}
              rowKey={(name): string => name}
              renderItem={(flowName, idx): React.ReactNode => (
                <FlowCell
                  idx={idx}
                  flowName={flowName}
                  emphasize={isCurrentCollection && currentFlow === flowName}
                  handleSelection={handleSelection}
                  handleDelete={handleDelete}
                  handleClone={handleClone}
                />
              )}
            />
            {provided.placeholder}
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );
}
Example #2
Source File: DragndropPostList.tsx    From clearflask with Apache License 2.0 6 votes vote down vote up
DragndropPostList = React.memo((props: {
  droppable?: boolean;
  droppableId: string;
  dragSelectedTourAnchorProps?: React.ComponentProps<typeof TourAnchor>;
} & React.ComponentProps<typeof PostList>) => {
  const { droppable, droppableId, ...PostListProps } = props;
  return (
    <Droppable droppableId={droppableId} isDropDisabled={!droppable}>
      {(providedDroppable, snapshotDroppable) => (
        <>
          <DragndropPostListDroppableInner
            providedDroppableInnerRef={providedDroppable.innerRef}
            PostListProps={PostListProps}
            DroppableProvidedProps={providedDroppable.droppableProps}
            dragSelectedTourAnchorProps={props.dragSelectedTourAnchorProps}
          />
          {providedDroppable.placeholder && (<div style={{ display: 'none' }}>{providedDroppable.placeholder}</div>)}
        </>
      )}
    </Droppable>
  );
}, customReactMemoEquals({ nested: new Set(['dragSelectedTourAnchorProps', 'PostListProps', 'DroppableProvidedProps']) }))
Example #3
Source File: AddButtonMappingDialog.tsx    From Pi-Tool with GNU General Public License v3.0 6 votes vote down vote up
ButtonActionCreator: React.FC<{ buttonDuration: ButtonPress }> = ({ buttonDuration }) => {
    const draggableId = uuidv4();

    return (
        <Droppable droppableId="PALETTE" direction="horizontal" isDropDisabled={true}>
            {(provided, _snapshot) => (
                <div ref={provided.innerRef} {...provided.droppableProps}>
                    <Box width={1} style={{ display: 'inline-flex', verticalAlign: 'middle', alignItems: 'center' }}>
                        <Draggable key={draggableId} draggableId={draggableId} index={buttonDuration as number}>

                            {(provided, snapshot) => (
                                <Box style={{ width: 50, height: 50 }}>
                                    <div
                                        style={{ width: 50, height: 50 }}
                                        ref={provided.innerRef}
                                        {...provided.draggableProps}
                                        {...provided.dragHandleProps}>

                                        <ButtonPressIcon press={buttonDuration} isButton={true} />
                                    </div>
                                    {snapshot.isDragging &&
                                        <ButtonPressIcon press={buttonDuration} isButton={true} />
                                    }
                                </Box>
                            )}
                        </Draggable>


                        <Typography>
                            {buttonDuration === ButtonPress.Short ? "Short button press" : "Long button press"}
                        </Typography>
                    </Box>
                </div>
            )}
        </Droppable>
    );
}
Example #4
Source File: next-item-list.tsx    From wikitrivia with MIT License 6 votes vote down vote up
export default function NextItemList(props: NextItemListProps) {
  const { next } = props;

  return (
    <div className={styles.container}>
      <Droppable droppableId="next" direction="horizontal">
        {(provided) => (
          <div className={styles.wrapper}>
            <div
              ref={provided.innerRef}
              {...provided.droppableProps}
              className={styles.list}
            >
              {next && (
                <ItemCard draggable index={0} item={next} key={next.id} />
              )}
              {provided.placeholder}
            </div>
          </div>
        )}
      </Droppable>
    </div>
  );
}
Example #5
Source File: SearchSidebar.tsx    From peterportal-client with MIT License 6 votes vote down vote up
SearchSidebar = () => {
  const dispatch = useAppDispatch();
  return (
    <div className="search-sidebar" >
      {isMobile && <div><CloseButton className='close-icon' onClick={() => { dispatch(setShowSearch(false)) }} /></div>}
      <div className='search-body'>
        <Droppable droppableId="search" type="COURSE">
          {(provided) => {
            return (
              <div
                ref={provided.innerRef}
                style={{ height: "100%" }}
                {...provided.droppableProps}
              >
                <div className='search-sidebar-content'>
                  <div className='search-sidebar-search-module'>
                    <SearchModule index='courses' />
                  </div>
                  <SearchHitContainer index='courses' CourseHitItem={CourseHitItem} />
                </div>
                {provided.placeholder}
              </div>
            );
          }}
        </Droppable>
      </div>
    </div>
  );
}
Example #6
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 #7
Source File: bodywrapper.tsx    From gant-design with MIT License 6 votes vote down vote up
BodyWrapper = ({ children, ...props }) => {
    const { onDragEnd } = useContext(TableBodyWrapperContext)

    return (
        <DragDropContext onDragEnd={onDragEnd} >
            <Droppable droppableId='droppable'>
                {
                    (provided, snapshot) => {
                        return (
                            <tbody {...props} ref={provided.innerRef} {...provided.droppableProps}>
                                {children}
                                {provided.placeholder}
                            </tbody>
                        )
                    }
                }
            </Droppable>
        </DragDropContext>
    )
}
Example #8
Source File: index.tsx    From S2 with MIT License 5 votes vote down vote up
Dimension: React.FC<DimensionProps> = React.memo((props) => {
  const {
    fieldType,
    crossRows,
    expandable,
    expandText = i18n('展开子项'),
    allowEmpty,
    items,
    droppableType,
    ...rest
  } = props;

  const [expandChildren, setExpandChildren] = React.useState(true);
  const SWITCHER_CONFIG = React.useMemo(getSwitcherConfig, []);

  const onUpdateExpand = (event: CheckboxChangeEvent) => {
    setExpandChildren(event.target.checked);
  };

  // 开启不允许为空后,如果当前有且仅有一个item时,需要禁用拖动
  const isDragDisabled = !allowEmpty && items.length === 1;

  const { text, icon: Icon } = SWITCHER_CONFIG[fieldType];
  return (
    <div
      className={cx(getSwitcherClassName(CLASS_NAME_PREFIX), {
        'long-dimension': crossRows,
      })}
    >
      <div className={getSwitcherClassName(CLASS_NAME_PREFIX, 'header')}>
        <div className="title">
          <Icon /> <span>{text}</span>
        </div>
        {expandable && (
          <div className={'expand-option'}>
            <Checkbox checked={expandChildren} onChange={onUpdateExpand} />
            <span className="description">{expandText}</span>
          </div>
        )}
      </div>

      <Droppable droppableId={fieldType} type={droppableType}>
        {(provided, snapshot) => (
          <div
            ref={provided.innerRef}
            {...provided.droppableProps}
            className={cx(getSwitcherClassName(CLASS_NAME_PREFIX, 'items'), {
              [getSwitcherClassName(CLASS_NAME_PREFIX, 'items-highlight')]:
                snapshot.isDraggingOver,
              [getSwitcherClassName(CLASS_NAME_PREFIX, 'long-items')]:
                crossRows,
            })}
          >
            {items.map((item: SwitcherItem, index: number) => (
              <DimensionItem
                key={item.id}
                index={index}
                fieldType={fieldType}
                item={item}
                expandable={expandable}
                expandChildren={expandChildren}
                isDragDisabled={isDragDisabled}
                {...rest}
              />
            ))}
            {provided.placeholder}
          </div>
        )}
      </Droppable>
    </div>
  );
})
Example #9
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 #10
Source File: DashboardQuickActions.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
QuickActionArea = (props: {
  droppableId: string;
  isDragging: boolean;
  feedback?: FeedbackInstance | null;
  enabled?: boolean;
  onClick?: (droppableId: string) => Promise<any>;
  color?: string;
  title?: string;
}) => {
  const { t } = useTranslation('app');
  const theme = useTheme();
  const classes = useStyles();
  const [autoDragging, setAutoDragging] = useState<boolean>(false);
  return (
    <RenderControl freezeInitialRender={props.isDragging}>
      <HoverArea>
        {(hoverAreaProps, isHovering, isHoverDown) => (
          <Droppable
            droppableId={props.droppableId}
            ignoreContainerClipping
            isDropDisabled={!props.enabled || (!isHovering && !autoDragging)}
          >
            {(provided, snapshot) => (
              <CardActionArea
                {...hoverAreaProps}
                ref={provided.innerRef}
                {...provided.droppableProps}
                disabled={!props.enabled}
                className={classNames(
                  classes.postAction,
                  !props.enabled && classes.postActionDisabled,
                )}
                style={!props.enabled ? {
                  color: theme.palette.text.disabled,
                } : {
                  color: props.color,
                  borderColor: props.color || fade(theme.palette.common.black, 0.54),
                  background: !snapshot.isDraggingOver ? undefined : fade(props.color || theme.palette.common.black, 0.1),
                }}
                onClick={async e => {
                  if (!props.enabled || !props.onClick) return;
                  setAutoDragging(true);
                  try {
                    await props.onClick(props.droppableId);
                  } finally {
                    setAutoDragging(false);
                  }
                }}
              >
                {provided.placeholder && (<div style={{ display: 'none' }}>{provided.placeholder}</div>)}
                {props.title && (
                  <Typography>{t(props.title as any)}</Typography>
                )}
              </CardActionArea>
            )}
          </Droppable>
        )}
      </HoverArea>
    </RenderControl>
  );
}
Example #11
Source File: index.tsx    From nextjs-hasura-fullstack with MIT License 5 votes vote down vote up
ListBoard: React.FC<ListBoardProps> = ({ list, index }) => {
  return (
    <Draggable draggableId={`${list.id}`} index={index} key={list.id}>
      {({ innerRef, dragHandleProps, draggableProps }, { isDragging }) => {
        return (
          <div
            ref={innerRef}
            {...draggableProps}
            className={`flex flex-col mx-4`}
          >
            <Card
              className={`w-72 bg-white flex flex-col ${
                isDragging && `border-purple-600`
              }`}
            >
              <CardHeader
                action={<ActionDropdown list={list} />}
                {...dragHandleProps}
                className={`hover:bg-purple-50`}
              >
                <ListHeader list={list} />
              </CardHeader>
              <Droppable
                type="CARD"
                direction="vertical"
                key={list.id}
                droppableId={`${list.id}`}
              >
                {(
                  { innerRef, droppableProps, placeholder },
                  { isDraggingOver },
                ) => (
                  <CardBody
                    ref={innerRef}
                    {...droppableProps}
                    className={`${isDraggingOver && `bg-purple-400 border-2`}`}
                  >
                    {list.cards.map((card, index) => (
                      <CardTask key={card.id} card={card} index={index} />
                    ))}
                    {placeholder}
                  </CardBody>
                )}
              </Droppable>

              <CardFooter className={`h-14`}>
                <NewCard
                  lastCard={list.cards[list.cards.length - 1]}
                  list={list}
                />
              </CardFooter>
            </Card>
          </div>
        )
      }}
    </Draggable>
  )
}
Example #12
Source File: Column.tsx    From gant-design with MIT License 5 votes vote down vote up
Column = (props) => {
    const {
        column,
        tasks,
        index,
        hideQuickAdd,
        isColumnDragDisabled,
        isTaskDropDisabled,
        ...nextProps
    } = props
    const { prefixCls, renderHeader, renderExtra, handleAddBtn, idKey, titleKey } = useContext(TaskBoardContext)

    return <Draggable
        index={index}
        draggableId={column[idKey]}
        isDragDisabled={isColumnDragDisabled}
    >
        {(provided, snapshot) => (
            <div
                className={prefixCls + '-column-wrapper'}
                ref={provided.innerRef}
                {...provided.draggableProps}
            >
                <div
                    className={prefixCls + '-column-container'}
                    style={{
                        boxShadow: `${snapshot.isDragging ? 'rgba(0, 0, 0, 0.2) 2px 2px 1px' : ''}`,
                    }}
                >
                    <div {...provided.dragHandleProps}>
                        {renderHeader === null ? null :
                            renderHeader ? renderHeader(column) :
                                <div className={prefixCls + '-column-header-wrapper'}>
                                    <Tooltip title={column[titleKey]} mouseEnterDelay={0.3} placement="topLeft">
                                        <div className={prefixCls + '-column-header-title'}>
                                            {column[titleKey]}{tasks && tasks.length > 0 ? `(${tasks.length})` : null}
                                        </div>
                                    </Tooltip>
                                    <div className={prefixCls + '-column-header-extra'}>
                                        {renderExtra && renderExtra(column)}
                                    </div>
                                </div>
                        }
                    </div>
                    <Droppable
                        droppableId={column[idKey]}
                        type='task'
                        isDropDisabled={isTaskDropDisabled}
                    >
                        {(provided, snapshot) => (
                            <div
                                className={prefixCls + '-task-drop-inner'}
                                ref={provided.innerRef}
                                {...provided.droppableProps}
                            >
                                <TaskList tasks={tasks} column={column} {...nextProps} />
                                {provided.placeholder}
                                {!hideQuickAdd && <div
                                    className={prefixCls + '-quick-add'}
                                    onClick={(e) => {
                                        e.stopPropagation()
                                        handleAddBtn && handleAddBtn(column)
                                    }}>
                                    <Icon type="plus" />
                                </div>}
                            </div>
                        )}
                    </Droppable>
                </div>
            </div>
        )}
    </Draggable>
}
Example #13
Source File: SidePanel.tsx    From TabMerger with GNU General Public License v3.0 5 votes vote down vote up
export default function SidePanel(): JSX.Element {
  const { available } = useSelector((state) => state.groups.present);
  const { inputValue, filterChoice } = useSelector((state) => state.header);
  const { dragType, isDragging } = useSelector((state) => state.dnd);

  const { filteredGroups, nonEmptyGroups } = useFilter();
  const groupSearch = inputValue !== "" && filterChoice === "group";
  const tabSearch = inputValue !== "" && filterChoice === "tab";
  const currentGroups = groupSearch ? filteredGroups : tabSearch ? nonEmptyGroups : available;
  const groupDrag = isGroupDrag(dragType);

  const groupsContainerRef = useRef<HTMLDivElement | null>(null);
  const containerHeight = useContainerHeight(groupsContainerRef);

  return (
    <Container>
      {currentGroups.map((group) => group.id).includes(available[0].id) && <Group {...available[0]} />}

      <div ref={groupsContainerRef}>
        <Droppable droppableId="sidePanel" isCombineEnabled={!groupDrag}>
          {(provider) => (
            <DraggableContainer
              ref={provider.innerRef}
              {...provider.droppableProps}
              $height={containerHeight}
              $dragging={isDragging && groupDrag}
            >
              {currentGroups
                .filter((group) => group.id !== available[0].id)
                .map((data, i) => (
                  <Draggable
                    key={data.id + i + 1}
                    draggableId={`group-${i + 1}`}
                    index={i + 1}
                    isDragDisabled={groupSearch}
                  >
                    {(provided, dragSnapshot) => (
                      <div ref={provided.innerRef} {...provided.draggableProps}>
                        <Group {...data} snapshot={dragSnapshot} dragHandleProps={provided.dragHandleProps} />
                      </div>
                    )}
                  </Draggable>
                ))}

              {provider.placeholder}
            </DraggableContainer>
          )}
        </Droppable>
      </div>
    </Container>
  );
}
Example #14
Source File: TabContainer.tsx    From yana with MIT License 5 votes vote down vote up
TabContainer: React.FC<{}> = props => {
  const theme = useTheme();
  const mainContent = useMainContentContext();

  return (
    <DragDropContext
      onDragEnd={(result, provided) => {
        if (result.destination?.index !== undefined) {
          mainContent.reorderTab(result.source.index, result.destination.index);
        }
      }}
    >
      <Droppable droppableId="tabs-droppable" direction="horizontal">
        {(provided, snapshot) => (
          <div className={styles.tabsContainer} ref={provided.innerRef} {...provided.droppableProps}>
            {mainContent.tabs.map((tab, idx) => {
              const id = tab.dataItem?.id ?? tab.page ?? 'unknown';
              const name = tab.dataItem?.name ?? (tab.page ? pages[tab.page]?.title : 'Unknown name');

              return (
                <Draggable key={id} draggableId={id} index={idx} disableInteractiveElementBlocking={true}>
                  {(provided, snapshot) => (
                    <div
                      ref={provided.innerRef}
                      {...provided.draggableProps}
                      {...provided.dragHandleProps}
                      className={cx(
                        styles.tab,
                        snapshot.isDragging && styles.draggingTab,
                        cxs({
                          color:
                            mainContent.openTabId === idx
                              ? 'white'
                              : Color(theme.topBarColor).mix(Color('#ffffff'), 0.5).toString(),
                          borderBottom: mainContent.openTabId === idx ? `4px solid ${theme.primaryColor}` : undefined,
                          fontWeight: mainContent.openTabId === idx ? 'bold' : 'normal',
                          backgroundColor: theme.topBarColor,
                          ':hover': {
                            borderBottom: mainContent.openTabId !== idx ? `4px solid white` : undefined,
                          },
                        })
                      )}
                      onClick={() => mainContent.activateTab(idx)}
                    >
                      {name.substr(0, 12)}
                      {name.length > 12 ? '...' : ''}
                      <div
                        className={cx(
                          styles.closeContainer,
                          cxs({
                            backgroundColor: theme.topBarColor,
                          })
                        )}
                        onClick={e => {
                          e.stopPropagation();
                          mainContent.closeTab(idx);
                        }}
                      >
                        <Icon icon={'cross'} />
                      </div>
                    </div>
                  )}
                </Draggable>
              );
            })}
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );
}
Example #15
Source File: Form.demo.dnd.tsx    From mantine with MIT License 5 votes vote down vote up
function Demo() {
  const form = useForm({
    initialValues: {
      employees: formList([
        { name: 'John Doe', email: '[email protected]' },
        { name: 'Bill Love', email: '[email protected]' },
        { name: 'Nancy Eagle', email: '[email protected]' },
        { name: 'Lim Notch', email: '[email protected]' },
        { name: 'Susan Seven', email: '[email protected]' },
      ]),
    },
  });

  const fields = form.values.employees.map((_, index) => (
    <Draggable key={index} index={index} draggableId={index.toString()}>
      {(provided) => (
        <Group ref={provided.innerRef} mt="xs" {...provided.draggableProps}>
          <Center {...provided.dragHandleProps}>
            <GripVertical size={18} />
          </Center>
          <TextInput
            placeholder="John Doe"
            {...form.getListInputProps('employees', index, 'name')}
          />
          <TextInput
            placeholder="[email protected]"
            {...form.getListInputProps('employees', index, 'email')}
          />
        </Group>
      )}
    </Draggable>
  ));

  return (
    <Box sx={{ maxWidth: 500 }} mx="auto">
      <DragDropContext
        onDragEnd={({ destination, source }) =>
          form.reorderListItem('employees', { from: source.index, to: destination.index })
        }
      >
        <Droppable droppableId="dnd-list" direction="vertical">
          {(provided) => (
            <div {...provided.droppableProps} ref={provided.innerRef}>
              {fields}
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>

      <Group position="center" mt="md">
        <Button onClick={() => form.addListItem('employees', { name: '', email: '' })}>
          Add employee
        </Button>
      </Group>

      <Text size="sm" weight={500} mt="md">
        Form values:
      </Text>
      <Code block>{JSON.stringify(form.values, null, 2)}</Code>
    </Box>
  );
}
Example #16
Source File: Todos.tsx    From max-todos with MIT License 5 votes vote down vote up
Todos = () => {
  const { todos, moveTodo } = useContext(MainContext)!;
  const [deleteSnackOpen, setDeleteSnackOpen] = useState(false);
  const [editSnackOpen, setEditSnackOpen] = useState(false);
  const [dragging, setDragging] = useState(false);
  const onDragEnd = (x: DropResult) => {
    if (!x.destination) return console.log(x);
    moveTodo(x.source.index, x.destination.index);
    setTimeout(() => setDragging(false), 200);
  };
  return (
    <>
      <DragDropContext
        onBeforeDragStart={() => setDragging(true)}
        onDragEnd={onDragEnd}
      >
        <Droppable droppableId="0">
          {(p) => (
            <div {...p.droppableProps} ref={p.innerRef}>
              <FlipMove disableAllAnimations={dragging}>
                {todos.map((todo, i) => {
                  return (
                    <Todo
                      todo={todo}
                      key={todo.id}
                      onDelete={() => setDeleteSnackOpen(true)}
                      index={i}
                      onEdit={() => setEditSnackOpen(true)}
                    />
                  );
                })}
              </FlipMove>
              {p.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>

      <Snackbar
        open={deleteSnackOpen}
        autoHideDuration={4000}
        onClose={() => setDeleteSnackOpen(false)}
        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
      >
        <Alert
          elevation={6}
          variant="filled"
          onClose={() => setDeleteSnackOpen(false)}
          severity="success"
        >
          Successfully deleted item!
        </Alert>
      </Snackbar>
      <Snackbar
        open={editSnackOpen}
        autoHideDuration={4000}
        onClose={() => setEditSnackOpen(false)}
        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
      >
        <Alert
          elevation={6}
          variant="filled"
          onClose={() => setEditSnackOpen(false)}
          severity="success"
        >
          Successfully edited item!
        </Alert>
      </Snackbar>
    </>
  );
}
Example #17
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 #18
Source File: StatOptionList.tsx    From slippi-stats with MIT License 5 votes vote down vote up
StatOptionList: React.FC<StatOptionListProps> = (props) => {
  const statOptions = props.value;
  const onDragEnd = (result: any) => {
    const { destination, source } = result;
    if (!destination) {
      return;
    }
    if (destination.droppableId === source.droppableId && destination.index === source.index) {
      return;
    }
    const newArray = reorder(props.value, source.index, destination.index);
    props.onChange(newArray);
  };

  const toggle = (statId: string) => {
    const optionIndex = statOptions.findIndex((o) => o.statId === statId);
    if (optionIndex === -1) {
      return;
    }
    const newOptions = Array.from(statOptions);
    const option = newOptions[optionIndex];
    option.enabled = !option.enabled;
    props.onChange(newOptions);
  };
  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="stat-option-list">
        {(provided) => (
          <div ref={provided.innerRef} {...provided.droppableProps}>
            {statOptions.map((option, i) => {
              return (
                <StatOptionItem
                  key={option.statId}
                  index={i}
                  id={option.statId}
                  checked={option.enabled}
                  onChange={() => toggle(option.statId)}
                />
              );
            })}
            {provided.placeholder}
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );
}
Example #19
Source File: DraggableTags.tsx    From nebula-studio with Apache License 2.0 5 votes vote down vote up
render() {
    const list = this.props.data.map(item => ({
      id: `field-${item}`,
      content: (
        <Tag className={styles.dragItem} closable={true} onClose={() => this.props.removeData(item)}>
          {item}
        </Tag>
      ),
    }));
    return (
      <DragDropContext onDragEnd={this.onDragEnd}>
        <Droppable droppableId="droppable" direction="horizontal">
          {(provided, _snapshot) => (
            <div
              {...provided.droppableProps}
              ref={provided.innerRef}
              style={{
                display: 'flex',
                overflow: 'auto',
                flexWrap: 'wrap',
              }}
            >
              {list.map((item, index) => (
                <Draggable key={item.id} draggableId={item.id} index={index}>
                  {(provided, snapshot) => (
                    <div
                      ref={provided.innerRef}
                      {...provided.draggableProps}
                      {...provided.dragHandleProps}
                      style={getItemStyle(
                        snapshot.isDragging,
                        provided.draggableProps.style,
                      )}
                    >
                      {item.content}
                    </div>
                  )}
                </Draggable>
              ))}
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    );
  }
Example #20
Source File: AddButtonMappingDialog.tsx    From Pi-Tool with GNU General Public License v3.0 5 votes vote down vote up
ButtonActionGrid: React.FC<{ buttonPresses: ButtonPressItems }> = ({ buttonPresses }) => {
    const classes = useStyles();


    return (
        <Paper className={classes.buttonActionGrid} variant="outlined">
            <Droppable
                droppableId="GRID"
                direction="horizontal"
                isDropDisabled={buttonPresses.length >= 8}>
                {(provided, snapshot) => (
                    <div
                        className={classes.buttonActionDroppable}
                        ref={provided.innerRef}
                        style={{ display: 'inline-flex', verticalAlign: 'middle', alignItems: 'center' }}
                        {...provided.droppableProps}>
                        <Box display="flex" flexDirection="row">
                            {buttonPresses.map((item, index) => (
                                <Draggable key={item.id} draggableId={item.id} index={index}>
                                    {(provided, snapshot) => (
                                        <div
                                            style={getStyle(provided.draggableProps.style, snapshot)}
                                            ref={provided.innerRef}
                                            {...provided.draggableProps}
                                            {...provided.dragHandleProps}>

                                            <ButtonPressIcon press={item.press} isButton={true} />
                                        </div>
                                    )}
                                </Draggable>
                            ))}

                        </Box>
                        {provided.placeholder}
                        {/* && (buttonPresses.length == 0 && <Typography>  Drag your your sequence here.</Typography>)} */}
                    </div>
                )}
            </Droppable>
        </Paper>
    );
}
Example #21
Source File: index.tsx    From Tiquet with MIT License 5 votes vote down vote up
List = ({ id, title, tasks, onDelete, editListTitle }: IProps): JSX.Element => {
  const [deleteModalOpen, setDeleteModalOpen] = useState(false);

  const handleDelete = (callback) => {
    onDelete()
    setDeleteModalOpen(false);
    trackEvent({
      category: 'Lists',
      action: 'Deleted',
      value: id
    });
    callback();
  };

  const getListStyles = () => ({
    width: '100%',
    minHeight: 5,
    maxHeight: 350,
    overflowY: 'auto',
    overflowX: 'hidden',
    display: 'flex',
    flexDirection: 'column',
  });

  const handleEditTitle = (value, callback) => {
    editListTitle(id, value);
    trackEvent({
      category: 'Lists',
      action: 'Title edited',
      value: id
    });
    callback();
  }

  return (
    <div className="list">
      {onDelete && (
        <Fragment>
          <ConfirmationModal
            isOpen={deleteModalOpen}
            title="DELETE LIST"
            description="Are you sure you want to delete this list?. All tasks will be lost."
            onSuccess={handleDelete}
            onCancel={() => setDeleteModalOpen(false)}
          />
          <i
            onClick={() => setDeleteModalOpen(true)}
            className="far fa-sm fa-trash-alt list__delete-icon"></i>
        </Fragment>
      )}
      <div className="list__header">
        <EditableText
          text={title}
          textClassName="list__header-title"
          tag="h5"
          onSuccess={handleEditTitle}
        />
      </div>
      <div className="list__body">
        <Droppable droppableId={new Number(id).toString()} key={`${title}_${id}`}>
          {(provided) => (
            <div
              {...provided.droppableProps}
              ref={provided.innerRef}
              style={getListStyles()}
            >
              {tasks.map((task: ITask, index: number) => (
                <Task key={task.uid} {...task} index={index} />
              ))}
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </div>
      <div className="list__footer">
        <CreateTask listId={id} />
      </div>
    </div>
  );
}
Example #22
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function MealsList({
  mealsForms,
  selectedVariantFormIndex,
  selectedVariantFormFieldId,
  headerHeight,
  onAddMeal,
  ...rest
}: Props) {
  const getMealNameInputRefById = useGetRefForId<HTMLInputElement>()
  const scrollTargetRef = useRef<HTMLDivElement>(null)

  const { onScrollToMeal } = useScrollToAndFocusMeal({
    scrollTargetRef,
    getMealNameInputRefById,
  })

  const dietFormActions = useDietFormActions()

  return (
    <Droppable droppableId="mealsList" type="mealsList">
      {(provided, snapshot) => (
        <Flex ref={provided.innerRef} flexDirection="column" {...rest}>
          {mealsForms.length > 0 ? (
            mealsForms.map((mealForm, index) => (
              <MealItem
                key={mealForm.fieldId}
                variantIndex={selectedVariantFormIndex}
                getMealNameInputRefById={getMealNameInputRefById}
                index={index}
                onRemove={dietFormActions.removeMealForm}
                mealForm={mealForm}
                onFirstAppear={onScrollToMeal}
                selectedVariantFormFieldId={selectedVariantFormFieldId}
                mb={5}
                isDragging={snapshot.isDraggingOver}
              />
            ))
          ) : (
            <EmptyList onAddMeal={onAddMeal} />
          )}

          {provided.placeholder}
          {mealsForms.length > 0 && (
            <>
              <MealsControls mealsForms={mealsForms} onAddMeal={onAddMeal} />
              <Box ref={scrollTargetRef} />
            </>
          )}
        </Flex>
      )}
    </Droppable>
  )
}
Example #23
Source File: played-item-list.tsx    From wikitrivia with MIT License 5 votes vote down vote up
export default function PlayedItemList(props: PlayedItemListProps) {
  const { badlyPlacedIndex, isDragging, items } = props;

  const [flippedId, setFlippedId] = React.useState<null | string>(null);

  React.useEffect(() => {
    if (isDragging && flippedId !== null) {
      setFlippedId(null);
    }
  }, [flippedId, isDragging]);

  return (
    <div className={styles.wrapper}>
      <div className={styles.listContainer}>
        <Droppable droppableId="played" direction="horizontal">
          {(provided) => (
            <div
              ref={provided.innerRef}
              {...provided.droppableProps}
              className={styles.list}
            >
              <div className={styles.timelineContainer}>
                <div className={styles.timeline}></div>
              </div>
              <div className={styles.items}>
                {items.map((item, index) => (
                  <ItemCard
                    draggable={badlyPlacedIndex !== null}
                    flippedId={flippedId}
                    index={index}
                    item={item}
                    key={item.id}
                    setFlippedId={setFlippedId}
                  />
                ))}
              </div>
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </div>
    </div>
  );
}
Example #24
Source File: NetworksDnd.tsx    From devex with GNU General Public License v3.0 5 votes vote down vote up
NetworksDnd: React.FC<IProps> = ({ cards, setCards, deleteNode, editNode }) => {

  const onDragEnd = (result: any) => {
    if (!result.destination) {
      return
    }
    const reorderedCards = reorder(
      cards,
      result.source.index,
      result.destination.index
    )
    setCards(reorderedCards)
  }

  return <Container>
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="droppable">
        {(provided, snapshot) => (
          <div
            className='dnd'
            {...provided.droppableProps}
            ref={provided.innerRef}
            style={getListStyle(snapshot.isDraggingOver)}
          >
            {cards.map((card, index) => (
              <Draggable key={card.id} draggableId={card.id.toString()} index={index}>
                {(provided, snapshot) => (
                  <div
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    style={getItemStyle(
                      snapshot.isDragging,
                      provided.draggableProps.style
                    )}
                  >
                    <NetworkCard
                      key={card.url}
                      url={card.url}
                      name={card.name}
                      deleteNode={deleteNode}
                      editNode={editNode}
                    />
                  </div>
                )}
              </Draggable>
            ))}
            {provided.placeholder}
          </div>
        )}
      </Droppable>
    </DragDropContext>
  </Container>
}
Example #25
Source File: designers.tsx    From ali-react-table with MIT License 5 votes vote down vote up
export function CheckedDimList({ style, className, pivot }: CheckDimListProps) {
  const dimMap = new Map(pivot.allDimensions.map((dim) => [dim.code, dim]))

  return (
    <CheckedDimListDiv style={style} className={className}>
      <DragDropContext
        onDragEnd={(result) => {
          if (!result.destination) {
            return
          }
          const i = result.source.index
          const j = result.destination.index
          const nextCodes = pivot.dimCodes.slice()
          const [code] = nextCodes.splice(i, 1)
          nextCodes.splice(j, 0, code)
          pivot.changeDimCodes(nextCodes)
        }}
      >
        <Droppable droppableId="dimension-list" direction="horizontal">
          {(dropProvided, snapshot) => (
            <div className="tag-list" ref={dropProvided.innerRef}>
              {pivot.dimCodes.map((dimCode, index) => {
                const allValues = pivot.dimValues[dimCode]
                const values = pivot.filters[dimCode]

                return (
                  <Draggable key={dimCode} draggableId={dimCode} index={index}>
                    {(dragProvided, snapshot) => (
                      <Overlay.Popup
                        key={dimCode}
                        trigger={
                          <div
                            key={dimCode}
                            ref={dragProvided.innerRef}
                            {...dragProvided.draggableProps}
                            {...dragProvided.dragHandleProps}
                            className={cx('tag', {
                              active: values.length < allValues.length,
                              empty: allValues.length > 0 && values.length === 0,
                            })}
                          >
                            {dimMap.get(dimCode).name}
                            <Filter16 className="filter-icon" />
                          </div>
                        }
                        triggerType="click"
                      >
                        <CheckedDimFilterPopup pivot={pivot} allValues={allValues} values={values} dimCode={dimCode} />
                      </Overlay.Popup>
                    )}
                  </Draggable>
                )
              })}
              {dropProvided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    </CheckedDimListDiv>
  )
}
Example #26
Source File: DataModelBranch.tsx    From datart with Apache License 2.0 4 votes vote down vote up
DataModelBranch: FC<{
  node: Column;
  onNodeTypeChange: (type: any, name: string) => void;
  onMoveToHierarchy: (node: Column) => void;
  onEditBranch;
  onDelete: (node: Column) => void;
  onDeleteFromHierarchy: (parent: Column) => (node: Column) => void;
}> = memo(
  ({
    node,
    onNodeTypeChange,
    onMoveToHierarchy,
    onEditBranch,
    onDelete,
    onDeleteFromHierarchy,
  }) => {
    const t = useI18NPrefix('view.model');
    const [isHover, setIsHover] = useState(false);

    const renderNode = (node, isDragging) => {
      let icon = (
        <FolderOpenOutlined style={{ alignSelf: 'center', color: YELLOW }} />
      );

      return (
        <>
          <div
            className="content"
            onMouseEnter={() => {
              setIsHover(true);
            }}
            onMouseLeave={() => {
              setIsHover(false);
            }}
          >
            <IW fontSize={FONT_SIZE_HEADING}>{icon}</IW>
            <span>{node.name}</span>
            <div className="action">
              {isHover && !isDragging && (
                <Tooltip title={t('rename')}>
                  <Button
                    type="link"
                    onClick={() => onEditBranch(node)}
                    icon={<EditOutlined />}
                  />
                </Tooltip>
              )}
              {isHover && !isDragging && (
                <Tooltip title={t('delete')}>
                  <Button
                    type="link"
                    onClick={() => onDelete(node)}
                    icon={<DeleteOutlined />}
                  />
                </Tooltip>
              )}
            </div>
          </div>
          <div className="children">
            {node?.children?.map(childNode => (
              <DataModelNode
                className="in-hierarchy"
                node={childNode}
                key={childNode.name}
                onMoveToHierarchy={onMoveToHierarchy}
                onNodeTypeChange={onNodeTypeChange}
                onDeleteFromHierarchy={onDeleteFromHierarchy(node)}
              />
            ))}
          </div>
        </>
      );
    };

    return (
      <Draggable
        key={node?.name}
        draggableId={node?.name}
        index={node?.index}
        isDragDisabled={true}
      >
        {(draggableProvided, draggableSnapshot) => {
          return (
            <StyledDataModelBranch
              ref={draggableProvided.innerRef}
              {...draggableProvided.draggableProps}
              {...draggableProvided.dragHandleProps}
            >
              <Droppable
                droppableId={node?.name}
                type={TreeNodeHierarchy.Branch}
                isCombineEnabled={false}
              >
                {(droppableProvided, droppableSnapshot) => (
                  <div ref={droppableProvided.innerRef}>
                    {renderNode(node, draggableSnapshot.isDragging)}
                    {droppableProvided.placeholder}
                  </div>
                )}
              </Droppable>
            </StyledDataModelBranch>
          );
        }}
      </Draggable>
    );
  },
)
Example #27
Source File: index.tsx    From nanolooker with MIT License 4 votes vote down vote up
CryptocurrencyPreferences: React.FC<Props> = ({ isDetailed }) => {
  const { t } = useTranslation();
  const {
    cryptocurrency,
    addCryptocurrency,
    removeCryptocurrency,
    reorderCryptocurrency,
  } = React.useContext(PreferencesContext);
  const [search, setSearch] = React.useState<string>("");

  const onSearch = (value: string) => {
    setSearch(value);
  };

  const onSelect = (value: string) => {
    const { symbol = "" } = dataSource.find(({ name }) => name === value) || {};

    addCryptocurrency(symbol);
    setSearch("");
  };

  const options = dataSource.map(({ name, symbol }) => (
    <Option
      key={name}
      value={name}
      symbol={symbol}
      disabled={cryptocurrency.includes(symbol) || symbol === "nano"}
    >
      <img
        src={`/cryptocurrencies/logo/${symbol}.png`}
        alt={name}
        width="16px"
        height="16px"
        style={{ marginRight: "6px" }}
      />
      {name}
    </Option>
  ));

  const reorder = (list: string[], startIndex: number, endIndex: number) => {
    const result = Array.from(list);
    const [removed] = result.splice(startIndex, 1);
    result.splice(endIndex, 0, removed);

    return result;
  };

  const onDragEnd = (result: any) => {
    const items = reorder(
      cryptocurrency,
      result.source?.index || 0,
      result.destination?.index || 0,
    );
    reorderCryptocurrency(items);
  };

  return (
    <Row>
      <Col xs={24}>
        <Text
          className={isDetailed ? "preference-detailed-title" : ""}
          style={{
            display: "block",
            marginBottom: "6px",
          }}
        >
          {t("preferences.watch")}
        </Text>
      </Col>
      {isDetailed ? (
        <Col xs={24} style={{ marginBottom: "6px" }}>
          <Text>{t("preferences.watchDetailed")}</Text>
        </Col>
      ) : null}

      <Col xs={24} md={isDetailed ? 12 : 24}>
        <AutoComplete
          value={search}
          style={{ width: "100%" }}
          filterOption={(value = "", option) => {
            const { value: name, symbol } = option as any;

            return (
              name.toLowerCase().includes(value.toLowerCase()) ||
              symbol.toLowerCase().includes(value.toLowerCase())
            );
          }}
          onSearch={onSearch}
          onSelect={onSelect}
          placeholder={t("preferences.watchSearch")}
        >
          {options}
        </AutoComplete>

        {cryptocurrency.length ? (
          <DragDropContext onDragEnd={onDragEnd}>
            <Droppable droppableId={`hello${isDetailed ? "-detailed" : ""}`}>
              {(provided, snapshot) => (
                <ul
                  style={{
                    margin: 0,
                    padding: "6px",
                    marginTop: "6px",
                    backgroundColor: snapshot.isDraggingOver
                      ? "#1890ff24"
                      : "#f6f6f6",
                    listStyle: "none",
                  }}
                  ref={provided.innerRef}
                  {...provided.droppableProps}
                >
                  {cryptocurrency.map((symbol, index) => {
                    const { name = "" } =
                      dataSource.find(
                        ({ symbol: sourceSymbol }) => sourceSymbol === symbol,
                      ) || {};
                    return (
                      <Draggable draggableId={name} index={index} key={name}>
                        {provided => {
                          // https://github.com/atlassian/react-beautiful-dnd/issues/1662#issuecomment-708538811
                          if (
                            typeof provided.draggableProps.onTransitionEnd ===
                            "function"
                          ) {
                            window?.requestAnimationFrame(() =>
                              // @ts-ignore
                              provided?.draggableProps?.onTransitionEnd?.({
                                propertyName: "transform",
                              }),
                            );
                          }

                          return (
                            <li
                              {...provided.draggableProps}
                              {...provided.dragHandleProps}
                              ref={provided.innerRef}
                            >
                              <div
                                style={{
                                  display: "flex",
                                  justifyContent: "space-between",
                                  alignItems: "center",
                                  width: "100%",
                                  padding: "6px",
                                  marginTop: "-1px",
                                  backgroundColor: "#fff",
                                  border: "1px solid #d9d9d9",
                                  ...(index !== cryptocurrency.length - 1
                                    ? { marginBottom: "6px" }
                                    : { marginBottom: "-1px" }),
                                }}
                              >
                                <span>
                                  <img
                                    src={`/cryptocurrencies/logo/${symbol}.png`}
                                    alt={name}
                                    width="16px"
                                    height="16px"
                                    style={{ marginRight: "6px" }}
                                  />
                                  {name}
                                </span>
                                <DeleteButton
                                  onClick={(e: Event) => {
                                    e.stopPropagation();
                                    removeCryptocurrency(symbol);
                                  }}
                                />
                              </div>
                            </li>
                          );
                        }}
                      </Draggable>
                    );
                  })}
                  {provided.placeholder}
                </ul>
              )}
            </Droppable>
          </DragDropContext>
        ) : null}
      </Col>
    </Row>
  );
}
Example #28
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>
  )
}
Example #29
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function VariantsList({
  onVariantFormSelect,
  onVariantFormCopy,
  forwardedRef = createRef(),
}: Props) {
  const scrollNodeRef = useRef<HTMLDivElement>(null)
  const screenSize = useScreenSize()
  const isPhone = screenSize <= ScreenSize.Small

  const nameModalDisclosure = useDisclosure()
  const detailsModalDisclosure = useDisclosure()

  const variantFormEvents = useVariantFormEvents({
    onVariantFormSelect,
    onVariantFormCopy,
    nameModalDisclosure,
    detailsModalDisclosure,
  })

  const dietForm = useDietForm()
  const getVariantItemRefById = useGetRefForId<HTMLDivElement>()
  const scrollState = useScrollState()

  return (
    <Flex py={6}>
      <AddVariantButton onClick={variantFormEvents.onAppend} />

      {!isPhone && (
        <VariantsMenuOrDrawer onVariantFormSelect={onVariantFormSelect} />
      )}

      <Droppable
        droppableId="variantsList"
        type="variantsList"
        direction="horizontal"
      >
        {provided => (
          <HFadeScroll
            onScrollStateChange={scrollState.onScrollStateChange}
            ref={mergeRefs([provided.innerRef, scrollNodeRef, forwardedRef])}
          >
            {dietForm.variantsForms.map((variantForm, index) => {
              return (
                <VariantItem
                  canRemove={dietForm.variantsForms.length > 1}
                  mr={2}
                  index={index}
                  onDelete={variantFormEvents.onRemove}
                  onEditName={variantFormEvents.onRename}
                  onClone={variantFormEvents.onCopy}
                  onViewDetails={variantFormEvents.onViewDetails}
                  key={variantForm.fieldId}
                  variantForm={variantForm}
                  isSelected={index === dietForm.selectedVariantFormIndex}
                  onSelect={variantFormEvents.onSelect}
                  ref={getVariantItemRefById(variantForm.fieldId)}
                >
                  {variantForm.name}
                </VariantItem>
              )
            })}

            {provided.placeholder}
          </HFadeScroll>
        )}
      </Droppable>

      {isPhone ? (
        <VariantsMenuOrDrawer onVariantFormSelect={onVariantFormSelect} />
      ) : (
        <ScrollButtons
          scrollNodeRef={scrollNodeRef}
          showsButtons={scrollState.showsScrollButtons}
          canScrollLeft={scrollState.canScrollLeft}
          canScrollRight={scrollState.canScrollRight}
        />
      )}

      <VariantNameModal
        isOpen={nameModalDisclosure.isOpen}
        onClose={nameModalDisclosure.onClose}
        variantFormIndex={variantFormEvents.variantFormIndex}
      />

      {variantFormEvents.variantForm && (
        <VariantsDetailsModal
          isOpen={detailsModalDisclosure.isOpen}
          onClose={detailsModalDisclosure.onClose}
          initialVariantForm={variantFormEvents.variantForm}
        />
      )}
    </Flex>
  )
}